i18n 1.8.11 → 1.9.1

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: 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