Hello community,

here is the log from the commit of package rubygem-i18n for openSUSE:Factory 
checked in at 2017-04-11 09:32:15
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/rubygem-i18n (Old)
 and      /work/SRC/openSUSE:Factory/.rubygem-i18n.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "rubygem-i18n"

Tue Apr 11 09:32:15 2017 rev:12 rq:479088 version:0.8.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/rubygem-i18n/rubygem-i18n.changes        
2015-01-29 09:57:15.000000000 +0100
+++ /work/SRC/openSUSE:Factory/.rubygem-i18n.new/rubygem-i18n.changes   
2017-04-11 09:32:16.482320922 +0200
@@ -1,0 +2,12 @@
+Mon Mar 13 15:28:26 UTC 2017 - [email protected]
+
+- Update to version 0.8.1
+  no changelog found
+
+-------------------------------------------------------------------
+Wed Feb  1 05:38:55 UTC 2017 - [email protected]
+
+- updated to version 0.8.0
+  no changelog found
+
+-------------------------------------------------------------------

Old:
----
  i18n-0.7.0.gem

New:
----
  gem2rpm.yml
  i18n-0.8.1.gem

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ rubygem-i18n.spec ++++++
--- /var/tmp/diff_new_pack.j0l3jT/_old  2017-04-11 09:32:17.670153162 +0200
+++ /var/tmp/diff_new_pack.j0l3jT/_new  2017-04-11 09:32:17.670153162 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package rubygem-i18n
 #
-# Copyright (c) 2015 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -16,17 +16,25 @@
 #
 
 
+#
+# This file was generated with a gem2rpm.yml and not just plain gem2rpm.
+# All sections marked as MANUAL, license headers, summaries and descriptions
+# can be maintained in that file. Please consult this file before editing any
+# of those fields
+#
+
 Name:           rubygem-i18n
-Version:        0.7.0
+Version:        0.8.1
 Release:        0
 %define mod_name i18n
 %define mod_full_name %{mod_name}-%{version}
 BuildRoot:      %{_tmppath}/%{name}-%{version}-build
+BuildRequires:  %{ruby >= 1.9.3}
 BuildRequires:  %{rubygem gem2rpm}
-BuildRequires:  %{ruby}
 BuildRequires:  ruby-macros >= 5
 Url:            http://github.com/svenfuchs/i18n
 Source:         http://rubygems.org/gems/%{mod_full_name}.gem
+Source1:        gem2rpm.yml
 Summary:        New wave Internationalization support for Ruby
 License:        MIT
 Group:          Development/Languages/Ruby
@@ -40,7 +48,7 @@
 
 %install
 %gem_install \
-  --doc-files="README.md MIT-LICENSE" \
+  --doc-files="MIT-LICENSE README.md" \
   -f
 
 %gem_packages

++++++ gem2rpm.yml ++++++
# ---
# ## used by gem2rpm
# :summary: this is a custom summary
# ## used by gem2rpm
# :description: |-
#   this is a custom description
#
#   it can be multiline
# ## used by gem2rpm
# :license: MIT or Ruby
# ## used by gem2rpm and gem_packages
# :version_suffix: -x_y
# ## used by gem2rpm and gem_packages
# :disable_docs: true
# ## used by gem2rpm
# :disable_automatic_rdoc_dep: true
# ## used by gem2rpm
# :preamble: |-
#   BuildRequires: foobar
#   Requires: foobar
# ## used by gem2rpm
# :patches:
#   foo.patch: -p1
#   bar.patch:
# :post_patch:
#   if you need to fiddle with the source dir before rebuilding the gem
# ## used by gem2rpm
:sources:
# - foo.desktop
# - bar.desktop
# :gem_install_args: '....'
# ## used by gem2rpm
# :pre_install: |-
#   %if 0%{?use_system_libev}
#   export USE_VENDORED_LIBEV="no"
#   %endif
# ## used by gem2rpm
# :post_install: |-
#   # delete custom files here or do other fancy stuff
#   install -D -m 0644 %{S:1} %{buildroot}%{_bindir}/gem2rpm-opensuse
# ## used by gem2rpm
# :testsuite_command: |-
#   (pushd %{buildroot}%{gem_base}/gems/%{mod_full_name} && rake test)
# ## used by gem2rpm
# :filelist: |-
#   /usr/bin/gem2rpm-opensuse
# ## used by gem2rpm
# :scripts:
#   :post: |-
#     /bin/echo foo
# ## used by gem_packages
# :main:
#   :preamble: |-
#     Requires: util-linux
#     Recommends: pwgen
#   :filelist: |-
#     /usr/bin/gem2rpm-opensuse
# ## used by gem_packages
# :custom_pkgs:
#   apache:
#     :preamble: |-
#       Requires: .....
#     :filelist: |-
#       /etc/apache2/conf.d/passenger.conf
#     :summary: Custom summary is optional
#     :description: |-
#       Custom description is optional
#
#       bar
#     :post: |-
#       /bin/echo foo
#
++++++ i18n-0.7.0.gem -> i18n-0.8.1.gem ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/README.md new/README.md
--- old/README.md       2014-12-19 18:06:15.000000000 +0100
+++ new/README.md       2017-02-22 03:51:26.000000000 +0100
@@ -4,6 +4,8 @@
 
 Ruby Internationalization and localization solution.
 
+[See the Rails Guide](http://guides.rubyonrails.org/i18n.html) for an example 
of its usage. (Note: This library can be used independently from Rails.)
+
 Features:
 
 * translation and localization
@@ -22,7 +24,7 @@
 * Cache
 * Pluralization: lambda pluralizers stored as translation data
 * Locale fallbacks, RFC4647 compliant (optionally: RFC4646 locale validation)
-* Gettext support
+* [Gettext support](https://github.com/svenfuchs/i18n/wiki/Gettext)
 * Translation metadata
 
 Alternative backends:
@@ -31,7 +33,7 @@
 * ActiveRecord (optionally: ActiveRecord::Missing and ActiveRecord::StoreProcs)
 * KeyValue (uses active_support/json and cannot store procs)
 
-For more information and lots of resources see: 
[http://ruby-i18n.org/wiki](http://ruby-i18n.org/wiki)
+For more information and lots of resources see [the 'Resources' page on the 
wiki](https://github.com/svenfuchs/i18n/wiki/Resources).
 
 ## Installation
 
@@ -71,7 +73,7 @@
 * [Joshua Harvey](http://www.workingwithrails.com/person/759-joshua-harvey)
 * [Stephan Soller](http://www.arkanis-development.de)
 * [Saimon Moore](http://saimonmoore.net)
-* [Matt Aimonetti](http://railsontherun.com)
+* [Matt Aimonetti](https://matt.aimonetti.net/)
 
 ## Contributors
 
Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gemfiles/Gemfile.rails-3.2.x 
new/gemfiles/Gemfile.rails-3.2.x
--- old/gemfiles/Gemfile.rails-3.2.x    2014-12-19 18:06:15.000000000 +0100
+++ new/gemfiles/Gemfile.rails-3.2.x    2017-02-22 03:51:26.000000000 +0100
@@ -6,3 +6,4 @@
 gem 'mocha'
 gem 'test_declarative'
 gem 'rake'
+gem 'minitest'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gemfiles/Gemfile.rails-3.2.x.lock 
new/gemfiles/Gemfile.rails-3.2.x.lock
--- old/gemfiles/Gemfile.rails-3.2.x.lock       2014-12-19 18:06:15.000000000 
+0100
+++ new/gemfiles/Gemfile.rails-3.2.x.lock       1970-01-01 01:00:00.000000000 
+0100
@@ -1,27 +0,0 @@
-PATH
-  remote: ..
-  specs:
-    i18n (0.7.0)
-
-GEM
-  remote: https://rubygems.org/
-  specs:
-    activesupport (3.2.21)
-      i18n (~> 0.6, >= 0.6.4)
-      multi_json (~> 1.0)
-    metaclass (0.0.4)
-    mocha (1.1.0)
-      metaclass (~> 0.0.1)
-    multi_json (1.10.1)
-    rake (10.4.2)
-    test_declarative (0.0.5)
-
-PLATFORMS
-  ruby
-
-DEPENDENCIES
-  activesupport (~> 3.2.0)
-  i18n!
-  mocha
-  rake
-  test_declarative
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gemfiles/Gemfile.rails-4.0.x 
new/gemfiles/Gemfile.rails-4.0.x
--- old/gemfiles/Gemfile.rails-4.0.x    2014-12-19 18:06:15.000000000 +0100
+++ new/gemfiles/Gemfile.rails-4.0.x    2017-02-22 03:51:26.000000000 +0100
@@ -6,3 +6,4 @@
 gem 'mocha'
 gem 'test_declarative'
 gem 'rake'
+gem 'minitest'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gemfiles/Gemfile.rails-4.0.x.lock 
new/gemfiles/Gemfile.rails-4.0.x.lock
--- old/gemfiles/Gemfile.rails-4.0.x.lock       2014-12-19 18:06:15.000000000 
+0100
+++ new/gemfiles/Gemfile.rails-4.0.x.lock       1970-01-01 01:00:00.000000000 
+0100
@@ -1,33 +0,0 @@
-PATH
-  remote: ..
-  specs:
-    i18n (0.7.0)
-
-GEM
-  remote: https://rubygems.org/
-  specs:
-    activesupport (4.0.12)
-      i18n (~> 0.6, >= 0.6.9)
-      minitest (~> 4.2)
-      multi_json (~> 1.3)
-      thread_safe (~> 0.1)
-      tzinfo (~> 0.3.37)
-    metaclass (0.0.4)
-    minitest (4.7.5)
-    mocha (1.1.0)
-      metaclass (~> 0.0.1)
-    multi_json (1.10.1)
-    rake (10.4.2)
-    test_declarative (0.0.5)
-    thread_safe (0.3.4)
-    tzinfo (0.3.42)
-
-PLATFORMS
-  ruby
-
-DEPENDENCIES
-  activesupport (~> 4.0.0)
-  i18n!
-  mocha
-  rake
-  test_declarative
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gemfiles/Gemfile.rails-4.1.x 
new/gemfiles/Gemfile.rails-4.1.x
--- old/gemfiles/Gemfile.rails-4.1.x    2014-12-19 18:06:15.000000000 +0100
+++ new/gemfiles/Gemfile.rails-4.1.x    2017-02-22 03:51:26.000000000 +0100
@@ -6,3 +6,4 @@
 gem 'mocha'
 gem 'test_declarative'
 gem 'rake'
+gem 'minitest'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gemfiles/Gemfile.rails-4.1.x.lock 
new/gemfiles/Gemfile.rails-4.1.x.lock
--- old/gemfiles/Gemfile.rails-4.1.x.lock       2014-12-19 18:06:15.000000000 
+0100
+++ new/gemfiles/Gemfile.rails-4.1.x.lock       1970-01-01 01:00:00.000000000 
+0100
@@ -1,34 +0,0 @@
-PATH
-  remote: ..
-  specs:
-    i18n (0.7.0)
-
-GEM
-  remote: https://rubygems.org/
-  specs:
-    activesupport (4.1.8)
-      i18n (~> 0.6, >= 0.6.9)
-      json (~> 1.7, >= 1.7.7)
-      minitest (~> 5.1)
-      thread_safe (~> 0.1)
-      tzinfo (~> 1.1)
-    json (1.8.1)
-    metaclass (0.0.4)
-    minitest (5.5.0)
-    mocha (1.1.0)
-      metaclass (~> 0.0.1)
-    rake (10.4.2)
-    test_declarative (0.0.5)
-    thread_safe (0.3.4)
-    tzinfo (1.2.2)
-      thread_safe (~> 0.1)
-
-PLATFORMS
-  ruby
-
-DEPENDENCIES
-  activesupport (~> 4.1.0)
-  i18n!
-  mocha
-  rake
-  test_declarative
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gemfiles/Gemfile.rails-4.2.x 
new/gemfiles/Gemfile.rails-4.2.x
--- old/gemfiles/Gemfile.rails-4.2.x    2014-12-19 18:06:15.000000000 +0100
+++ new/gemfiles/Gemfile.rails-4.2.x    2017-02-22 03:51:26.000000000 +0100
@@ -2,7 +2,8 @@
 
 gemspec :path => '..'
 
-gem 'activesupport', '~> 4.2.0.rc3'
+gem 'activesupport', '~> 4.2.0'
 gem 'mocha'
 gem 'test_declarative'
 gem 'rake'
+gem 'minitest'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gemfiles/Gemfile.rails-4.2.x.lock 
new/gemfiles/Gemfile.rails-4.2.x.lock
--- old/gemfiles/Gemfile.rails-4.2.x.lock       2014-12-19 18:06:15.000000000 
+0100
+++ new/gemfiles/Gemfile.rails-4.2.x.lock       1970-01-01 01:00:00.000000000 
+0100
@@ -1,34 +0,0 @@
-PATH
-  remote: ..
-  specs:
-    i18n (0.7.0)
-
-GEM
-  remote: https://rubygems.org/
-  specs:
-    activesupport (4.2.0.rc3)
-      i18n (>= 0.7.0.beta1, < 0.8)
-      json (~> 1.7, >= 1.7.7)
-      minitest (~> 5.1)
-      thread_safe (~> 0.1)
-      tzinfo (~> 1.1)
-    json (1.8.1)
-    metaclass (0.0.4)
-    minitest (5.5.0)
-    mocha (1.1.0)
-      metaclass (~> 0.0.1)
-    rake (10.4.2)
-    test_declarative (0.0.5)
-    thread_safe (0.3.4)
-    tzinfo (1.2.2)
-      thread_safe (~> 0.1)
-
-PLATFORMS
-  ruby
-
-DEPENDENCIES
-  activesupport (~> 4.2.0.rc3)
-  i18n!
-  mocha
-  rake
-  test_declarative
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gemfiles/Gemfile.rails-5.0.x 
new/gemfiles/Gemfile.rails-5.0.x
--- old/gemfiles/Gemfile.rails-5.0.x    1970-01-01 01:00:00.000000000 +0100
+++ new/gemfiles/Gemfile.rails-5.0.x    2017-02-22 03:51:26.000000000 +0100
@@ -0,0 +1,9 @@
+source 'https://rubygems.org'
+
+gemspec :path => '..'
+
+gem 'activesupport', '~> 5.0.0'
+gem 'mocha'
+gem 'test_declarative'
+gem 'rake'
+gem 'minitest'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gemfiles/Gemfile.rails-master 
new/gemfiles/Gemfile.rails-master
--- old/gemfiles/Gemfile.rails-master   2014-12-19 18:06:15.000000000 +0100
+++ new/gemfiles/Gemfile.rails-master   2017-02-22 03:51:26.000000000 +0100
@@ -6,3 +6,4 @@
 gem 'mocha'
 gem 'test_declarative'
 gem 'rake'
+gem 'minitest'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gemfiles/Gemfile.rails-master.lock 
new/gemfiles/Gemfile.rails-master.lock
--- old/gemfiles/Gemfile.rails-master.lock      2014-12-19 18:06:15.000000000 
+0100
+++ new/gemfiles/Gemfile.rails-master.lock      1970-01-01 01:00:00.000000000 
+0100
@@ -1,40 +0,0 @@
-GIT
-  remote: git://github.com/rails/rails.git
-  revision: fe46f009be1ece58e45abc51195e2381a71bd023
-  branch: master
-  specs:
-    activesupport (5.0.0.alpha)
-      i18n (>= 0.7.0.beta1, < 0.8)
-      json (~> 1.7, >= 1.7.7)
-      minitest (~> 5.1)
-      thread_safe (~> 0.1)
-      tzinfo (~> 1.1)
-
-PATH
-  remote: ..
-  specs:
-    i18n (0.7.0)
-
-GEM
-  remote: https://rubygems.org/
-  specs:
-    json (1.8.1)
-    metaclass (0.0.4)
-    minitest (5.5.0)
-    mocha (1.1.0)
-      metaclass (~> 0.0.1)
-    rake (10.4.2)
-    test_declarative (0.0.5)
-    thread_safe (0.3.4)
-    tzinfo (1.2.2)
-      thread_safe (~> 0.1)
-
-PLATFORMS
-  ruby
-
-DEPENDENCIES
-  activesupport!
-  i18n!
-  mocha
-  rake
-  test_declarative
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/i18n/backend/base.rb new/lib/i18n/backend/base.rb
--- old/lib/i18n/backend/base.rb        2014-12-19 18:06:15.000000000 +0100
+++ new/lib/i18n/backend/base.rb        2017-02-22 03:51:26.000000000 +0100
@@ -25,20 +25,32 @@
         raise InvalidLocale.new(locale) unless locale
         entry = key && lookup(locale, key, options[:scope], options)
 
-        if options.empty?
-          entry = resolve(locale, key, entry, options)
+        if entry.nil? && options.key?(:default)
+          entry = default(locale, key, options[:default], options)
         else
-          count, default = options.values_at(:count, :default)
-          values = options.except(*RESERVED_KEYS)
-          entry = entry.nil? && default ?
-            default(locale, key, default, options) : resolve(locale, key, 
entry, options)
+          entry = resolve(locale, key, entry, options)
+        end
+
+        if entry.nil?
+          if (options.key?(:default) && !options[:default].nil?) || 
!options.key?(:default)
+            throw(:exception, I18n::MissingTranslation.new(locale, key, 
options))
+          end
         end
 
-        throw(:exception, I18n::MissingTranslation.new(locale, key, options)) 
if entry.nil?
         entry = entry.dup if entry.is_a?(String)
 
+        count = options[:count]
         entry = pluralize(locale, entry, count) if count
-        entry = interpolate(locale, entry, values) if values
+
+        deep_interpolation = options[:deep_interpolation]
+        values = options.except(*RESERVED_KEYS)
+        if values
+          entry = if deep_interpolation
+            deep_interpolate(locale, entry, values)
+          else
+            interpolate(locale, entry, values)
+          end
+        end
         entry
       end
 
@@ -50,6 +62,9 @@
       # format string. Takes a key from the date/time formats translations as
       # a format argument (<em>e.g.</em>, <tt>:short</tt> in 
<tt>:'date.formats'</tt>).
       def localize(locale, object, format = :default, options = {})
+        if object.nil? && options.include?(:default)
+          return options[:default]
+        end
         raise ArgumentError, "Object must be a Date, DateTime or Time object. 
#{object.inspect} given." unless object.respond_to?(:strftime)
 
         if Symbol === format
@@ -59,18 +74,7 @@
           format  = I18n.t(:"#{type}.formats.#{key}", options)
         end
 
-        # format = resolve(locale, object, format, options)
-        format = format.to_s.gsub(/%[aAbBpP]/) do |match|
-          case match
-          when '%a' then I18n.t(:"date.abbr_day_names",                  
:locale => locale, :format => format)[object.wday]
-          when '%A' then I18n.t(:"date.day_names",                       
:locale => locale, :format => format)[object.wday]
-          when '%b' then I18n.t(:"date.abbr_month_names",                
:locale => locale, :format => format)[object.mon]
-          when '%B' then I18n.t(:"date.month_names",                     
:locale => locale, :format => format)[object.mon]
-          when '%p' then I18n.t(:"time.#{object.hour < 12 ? :am : :pm}", 
:locale => locale, :format => format).upcase if object.respond_to? :hour
-          when '%P' then I18n.t(:"time.#{object.hour < 12 ? :am : :pm}", 
:locale => locale, :format => format).downcase if object.respond_to? :hour
-          end
-        end
-
+        format = translate_localization_format(locale, object, format, options)
         object.strftime(format)
       end
 
@@ -155,6 +159,30 @@
           end
         end
 
+        # Deep interpolation
+        #
+        #   deep_interpolate { people: { ann: "Ann is %{ann}", john: "John is 
%{john}" } },
+        #                    ann: 'good', john: 'big'
+        #   #=> { people: { ann: "Ann is good", john: "John is big" } }
+        def deep_interpolate(locale, data, values = {})
+          return data if values.empty?
+
+          case data
+          when ::String
+            I18n.interpolate(data, values)
+          when ::Hash
+            data.each_with_object({}) do |(k, v), result|
+              result[k] = deep_interpolate(locale, v, values)
+            end
+          when ::Array
+            data.map do |v|
+              deep_interpolate(locale, v, values)
+            end
+          else
+            data
+          end
+        end
+
         # Loads a single translations file by delegating to #load_rb or
         # #load_yml depending on the file extension and directly merges the
         # data to the existing translations. Raises I18n::UnknownFileType
@@ -184,6 +212,19 @@
             raise InvalidLocaleData.new(filename, e.inspect)
           end
         end
+
+        def translate_localization_format(locale, object, format, options)
+          format.to_s.gsub(/%[aAbBpP]/) do |match|
+            case match
+            when '%a' then I18n.t(:"date.abbr_day_names",                  
:locale => locale, :format => format)[object.wday]
+            when '%A' then I18n.t(:"date.day_names",                       
:locale => locale, :format => format)[object.wday]
+            when '%b' then I18n.t(:"date.abbr_month_names",                
:locale => locale, :format => format)[object.mon]
+            when '%B' then I18n.t(:"date.month_names",                     
:locale => locale, :format => format)[object.mon]
+            when '%p' then I18n.t(:"time.#{object.hour < 12 ? :am : :pm}", 
:locale => locale, :format => format).upcase if object.respond_to? :hour
+            when '%P' then I18n.t(:"time.#{object.hour < 12 ? :am : :pm}", 
:locale => locale, :format => format).downcase if object.respond_to? :hour
+            end
+          end
+        end
     end
   end
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/i18n/backend/cache.rb 
new/lib/i18n/backend/cache.rb
--- old/lib/i18n/backend/cache.rb       2014-12-19 18:06:15.000000000 +0100
+++ new/lib/i18n/backend/cache.rb       2017-02-22 03:51:26.000000000 +0100
@@ -13,9 +13,13 @@
 # You can use any cache implementation you want that provides the same API as
 # ActiveSupport::Cache (only the methods #fetch and #write are being used).
 #
-# The cache_key implementation assumes that you only pass values to
-# I18n.translate that return a valid key from #hash (see
-# http://www.ruby-doc.org/core/classes/Object.html#M000337).
+# The cache_key implementation by default assumes you pass values that return
+# a valid key from #hash (see 
+# http://www.ruby-doc.org/core/classes/Object.html#M000337). However, you can 
+# configure your own digest method via which responds to #hexdigest (see 
+# http://ruby-doc.org/stdlib/libdoc/digest/rdoc/index.html):
+#
+#   I18n.cache_key_digest = Digest::MD5.new
 #
 # If you use a lambda as a default value in your translation like this:
 #
@@ -37,6 +41,7 @@
   class << self
     @@cache_store = nil
     @@cache_namespace = nil
+    @@cache_key_digest = nil
 
     def cache_store
       @@cache_store
@@ -54,6 +59,14 @@
       @@cache_namespace = namespace
     end
 
+    def cache_key_digest
+      @@cache_key_digest
+    end
+
+    def cache_key_digest=(key_digest)
+      @@cache_key_digest = key_digest
+    end
+
     def perform_caching?
       !cache_store.nil?
     end
@@ -76,7 +89,8 @@
         end
 
         def _fetch(cache_key, &block)
-          result = I18n.cache_store.read(cache_key) and return result
+          result = I18n.cache_store.read(cache_key)
+          return result unless result.nil?
           result = catch(:exception, &block)
           I18n.cache_store.write(cache_key, result) unless result.is_a?(Proc)
           result
@@ -84,13 +98,17 @@
 
         def cache_key(locale, key, options)
           # This assumes that only simple, native Ruby values are passed to 
I18n.translate.
-          
"i18n/#{I18n.cache_namespace}/#{locale}/#{key.hash}/#{USE_INSPECT_HASH ? 
options.inspect.hash : options.hash}"
+          
"i18n/#{I18n.cache_namespace}/#{locale}/#{digest_item(key)}/#{USE_INSPECT_HASH 
? digest_item(options.inspect) : digest_item(options)}"
         end
 
       private
         # In Ruby < 1.9 the following is true: { :foo => 1, :bar => 2 }.hash 
== { :foo => 2, :bar => 1 }.hash
         # Therefore we must use the hash of the inspect string instead to 
avoid cache key colisions.
         USE_INSPECT_HASH = RUBY_VERSION <= "1.9"
+
+        def digest_item(key)
+          I18n.cache_key_digest ? I18n.cache_key_digest.hexdigest(key.to_s) : 
key.hash
+        end
     end
   end
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/i18n/backend/chain.rb 
new/lib/i18n/backend/chain.rb
--- old/lib/i18n/backend/chain.rb       2014-12-19 18:06:15.000000000 +0100
+++ new/lib/i18n/backend/chain.rb       2017-02-22 03:51:26.000000000 +0100
@@ -17,7 +17,7 @@
     class Chain
       module Implementation
         include Base
-        
+
         attr_accessor :backends
 
         def initialize(*backends)
@@ -46,7 +46,7 @@
               translation = backend.translate(locale, key, options)
               if namespace_lookup?(translation, options)
                 namespace = _deep_merge(translation, namespace || {})
-              elsif !translation.nil?
+              elsif !translation.nil? || (options.key?(:default) && 
options[:default].nil?)
                 return translation
               end
             end
@@ -75,7 +75,7 @@
           def namespace_lookup?(result, options)
             result.is_a?(Hash) && !options.has_key?(:count)
           end
-          
+
         private
           # This is approximately what gets used in ActiveSupport.
           # However since we are not guaranteed to run in an ActiveSupport 
context
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/i18n/backend/fallbacks.rb 
new/lib/i18n/backend/fallbacks.rb
--- old/lib/i18n/backend/fallbacks.rb   2014-12-19 18:06:15.000000000 +0100
+++ new/lib/i18n/backend/fallbacks.rb   2017-02-22 03:51:26.000000000 +0100
@@ -35,21 +35,25 @@
       # it's a Symbol. When the default contains a String, Proc or Hash
       # it is evaluated last after all the fallback locales have been tried.
       def translate(locale, key, options = {})
-        return super if options[:fallback]
+        return super unless options.fetch(:fallback, true)
+        return super if (@fallback_locked ||= false)
         default = extract_non_symbol_default!(options) if options[:default]
 
-        options[:fallback] = true
-        I18n.fallbacks[locale].each do |fallback|
-          begin
-            catch(:exception) do
-              result = super(fallback, key, options)
-              return result unless result.nil?
+        begin
+          @fallback_locked = true
+          I18n.fallbacks[locale].each do |fallback|
+            begin
+              catch(:exception) do
+                result = super(fallback, key, options)
+                return result if (result.nil? && options.key?(:default) && 
options[:default].nil?) || !result.nil?
+              end
+            rescue I18n::InvalidLocale
+              # we do nothing when the locale is invalid, as this is a 
fallback anyways.
             end
-          rescue I18n::InvalidLocale
-            # we do nothing when the locale is invalid, as this is a fallback 
anyways.
           end
+        ensure
+          @fallback_locked = false
         end
-        options.delete(:fallback)
 
         return super(locale, nil, options.merge(:default => default)) if 
default
         throw(:exception, I18n::MissingTranslation.new(locale, key, options))
@@ -64,6 +68,17 @@
         return first_non_symbol_default
       end
 
+      def exists?(locale, key)
+        I18n.fallbacks[locale].each do |fallback|
+          begin
+            return true if super(fallback, key)
+          rescue I18n::InvalidLocale
+            # we do nothing when the locale is invalid, as this is a fallback 
anyways.
+          end
+        end
+
+        false
+      end
     end
   end
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/i18n/backend/gettext.rb 
new/lib/i18n/backend/gettext.rb
--- old/lib/i18n/backend/gettext.rb     2014-12-19 18:06:15.000000000 +0100
+++ new/lib/i18n/backend/gettext.rb     2017-02-22 03:51:26.000000000 +0100
@@ -1,24 +1,33 @@
 require 'i18n/gettext'
 require 'i18n/gettext/po_parser'
 
-# Experimental support for using Gettext po files to store translations.
-#
-# To use this you can simply include the module to the Simple backend - or
-# whatever other backend you are using.
-#
-#   I18n::Backend::Simple.include(I18n::Backend::Gettext)
-#
-# Now you should be able to include your Gettext translation (*.po) files to
-# the I18n.load_path so they're loaded to the backend and you can use them as
-# usual:
-#
-#  I18n.load_path += Dir["path/to/locales/*.po"]
-#
-# Following the Gettext convention this implementation expects that your
-# translation files are named by their locales. E.g. the file en.po would
-# contain the translations for the English locale.
 module I18n
   module Backend
+    # Experimental support for using Gettext po files to store translations.
+    #
+    # To use this you can simply include the module to the Simple backend - or
+    # whatever other backend you are using.
+    #
+    #  I18n::Backend::Simple.include(I18n::Backend::Gettext)
+    #
+    # Now you should be able to include your Gettext translation (*.po) files 
to
+    # the +I18n.load_path+ so they're loaded to the backend and you can use 
them as
+    # usual:
+    #
+    #  I18n.load_path += Dir["path/to/locales/*.po"]
+    #
+    # Following the Gettext convention this implementation expects that your
+    # translation files are named by their locales. E.g. the file en.po would
+    # contain the translations for the English locale.
+    #
+    # To translate text <b>you must use</b> one of the translate methods 
provided by
+    # I18n::Gettext::Helpers.
+    #
+    #  include I18n::Gettext::Helpers
+    #  puts _("some string")
+    #
+    # Without it strings containing periods (".") will not be translated.
+
     module Gettext
       class PoData < Hash
         def set_comment(msgid_or_sym, comment)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/i18n/backend/metadata.rb 
new/lib/i18n/backend/metadata.rb
--- old/lib/i18n/backend/metadata.rb    2014-12-19 18:06:15.000000000 +0100
+++ new/lib/i18n/backend/metadata.rb    2017-02-22 03:51:26.000000000 +0100
@@ -21,11 +21,15 @@
         def included(base)
           Object.class_eval do
             def translation_metadata
-              @translation_metadata ||= {}
+              unless self.frozen?
+                @translation_metadata ||= {}
+              else
+                {}
+              end
             end
 
             def translation_metadata=(translation_metadata)
-              @translation_metadata = translation_metadata
+              @translation_metadata = translation_metadata unless self.frozen?
             end
           end unless Object.method_defined?(:translation_metadata)
         end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/i18n/backend/simple.rb 
new/lib/i18n/backend/simple.rb
--- old/lib/i18n/backend/simple.rb      2014-12-19 18:06:15.000000000 +0100
+++ new/lib/i18n/backend/simple.rb      2017-02-22 03:51:26.000000000 +0100
@@ -63,7 +63,7 @@
         end
 
         # Looks up a translation from the translations hash. Returns nil if
-        # eiher key is nil, or locale, scope or key do not exist as a key in 
the
+        # either key is nil, or locale, scope or key do not exist as a key in 
the
         # nested translations hash. Splits keys or scopes containing dots
         # into multiple keys, i.e. <tt>currency.format</tt> is regarded the 
same as
         # <tt>%w(currency format)</tt>.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/i18n/backend/transliterator.rb 
new/lib/i18n/backend/transliterator.rb
--- old/lib/i18n/backend/transliterator.rb      2014-12-19 18:06:15.000000000 
+0100
+++ new/lib/i18n/backend/transliterator.rb      2017-02-22 03:51:26.000000000 
+0100
@@ -71,13 +71,14 @@
 
         def initialize(rule = nil)
           @rule = rule
-          add DEFAULT_APPROXIMATIONS.dup
+          add_default_approximations
           add rule if rule
         end
 
         def transliterate(string, replacement = nil)
+          replacement ||= DEFAULT_REPLACEMENT_CHAR
           string.gsub(/[^\x00-\x7f]/u) do |char|
-            approximations[char] || replacement || DEFAULT_REPLACEMENT_CHAR
+            approximations[char] || replacement
           end
         end
 
@@ -87,6 +88,12 @@
           @approximations ||= {}
         end
 
+        def add_default_approximations
+          DEFAULT_APPROXIMATIONS.each do |key, value|
+            approximations[key] = value
+          end
+        end
+
         # Add transliteration rules to the approximations hash.
         def add(hash)
           hash.each do |key, value|
@@ -96,4 +103,4 @@
       end
     end
   end
-end
\ No newline at end of file
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/i18n/config.rb new/lib/i18n/config.rb
--- old/lib/i18n/config.rb      2014-12-19 18:06:15.000000000 +0100
+++ new/lib/i18n/config.rb      2017-02-22 03:51:26.000000000 +0100
@@ -5,7 +5,7 @@
     # The only configuration value that is not global and scoped to thread is 
:locale.
     # It defaults to the default_locale.
     def locale
-      @locale ||= default_locale
+      defined?(@locale) && @locale ? @locale : default_locale
     end
 
     # Sets the current locale pseudo-globally, i.e. in the Thread.current hash.
@@ -124,6 +124,8 @@
     # behave like a Ruby Array.
     def load_path=(load_path)
       @@load_path = load_path
+      @@available_locales_set = nil
+      backend.reload!
     end
 
     # Whether or not to verify if locales are in the list of available locales.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/i18n/core_ext/hash.rb 
new/lib/i18n/core_ext/hash.rb
--- old/lib/i18n/core_ext/hash.rb       2014-12-19 18:06:15.000000000 +0100
+++ new/lib/i18n/core_ext/hash.rb       2017-02-22 03:51:26.000000000 +0100
@@ -1,7 +1,7 @@
 class Hash
   def slice(*keep_keys)
-    h = {}
-    keep_keys.each { |key| h[key] = fetch(key) }
+    h = self.class.new
+    keep_keys.each { |key| h[key] = fetch(key) if has_key?(key) }
     h
   end unless Hash.method_defined?(:slice)
 
@@ -21,7 +21,7 @@
   MERGER = proc do |key, v1, v2|
     Hash === v1 && Hash === v2 ? v1.merge(v2, &MERGER) : v2
   end
-  
+
   def deep_merge!(data)
     merge!(data, &MERGER)
   end unless Hash.method_defined?(:deep_merge!)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/i18n/exceptions.rb new/lib/i18n/exceptions.rb
--- old/lib/i18n/exceptions.rb  2014-12-19 18:06:15.000000000 +0100
+++ new/lib/i18n/exceptions.rb  2017-02-22 03:51:26.000000000 +0100
@@ -38,12 +38,12 @@
     end
   end
 
-  class MissingTranslation
+  class MissingTranslation < ArgumentError
     module Base
       attr_reader :locale, :key, :options
 
-      def initialize(locale, key, options = nil)
-        @key, @locale, @options = key, locale, options.dup || {}
+      def initialize(locale, key, options = {})
+        @key, @locale, @options = key, locale, options.dup
         options.each { |k, v| self.options[k] = v.inspect if v.is_a?(Proc) }
       end
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/i18n/gettext/helpers.rb 
new/lib/i18n/gettext/helpers.rb
--- old/lib/i18n/gettext/helpers.rb     2014-12-19 18:06:15.000000000 +0100
+++ new/lib/i18n/gettext/helpers.rb     2017-02-22 03:51:26.000000000 +0100
@@ -7,6 +7,15 @@
     #
     #   include I18n::Gettext::Helpers
     module Helpers
+      # Makes dynamic translation messages readable for the gettext parser.
+      # <tt>_(fruit)</tt> cannot be understood by the gettext parser. To help 
the parser find all your translations,
+      # you can add <tt>fruit = N_("Apple")</tt> which does not translate, but 
tells the parser: "Apple" needs translation.
+      # * msgid: the message id.
+      # * Returns: msgid.
+      def N_(msgsid)
+        msgsid
+      end
+
       def gettext(msgid, options = {})
         I18n.t(msgid, { :default => msgid, :separator => '|' }.merge(options))
       end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/i18n/interpolate/ruby.rb 
new/lib/i18n/interpolate/ruby.rb
--- old/lib/i18n/interpolate/ruby.rb    2014-12-19 18:06:15.000000000 +0100
+++ new/lib/i18n/interpolate/ruby.rb    2017-02-22 03:51:26.000000000 +0100
@@ -22,7 +22,7 @@
         if match == '%%'
           '%'
         else
-          key = ($1 || $2).to_sym
+          key = ($1 || $2 || match.tr("%{}", "")).to_sym
           value = if values.key?(key)
                     values[key]
                   else
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/i18n/tests/basics.rb new/lib/i18n/tests/basics.rb
--- old/lib/i18n/tests/basics.rb        2014-12-19 18:06:15.000000000 +0100
+++ new/lib/i18n/tests/basics.rb        2017-02-22 03:51:26.000000000 +0100
@@ -37,8 +37,9 @@
       end
 
       test "available_locales delegates to the backend when not set 
explicitely" do
-        I18n.backend.expects(:available_locales).twice
-        assert_equal I18n.available_locales, I18n.available_locales
+        original_available_locales_value = I18n.backend.available_locales
+        
I18n.backend.expects(:available_locales).returns(original_available_locales_value).twice
+        assert_equal I18n.backend.available_locales, I18n.available_locales
       end
 
       test "exists? is implemented by the backend" do
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/i18n/tests/defaults.rb 
new/lib/i18n/tests/defaults.rb
--- old/lib/i18n/tests/defaults.rb      2014-12-19 18:06:15.000000000 +0100
+++ new/lib/i18n/tests/defaults.rb      2017-02-22 03:51:26.000000000 +0100
@@ -24,6 +24,14 @@
         assert_equal 'bar', I18n.t(:does_not_exist, :default => 
[:does_not_exist_2, :'foo.bar'])
       end
 
+      test "defaults: given false it returns false" do
+        assert_equal false, I18n.t(:does_not_exist, :default => false)
+      end
+
+      test "defaults: given nil it returns nil" do
+        assert_nil I18n.t(:does_not_exist, :default => nil)
+      end
+
       test "defaults: given an array of missing keys it raises a 
MissingTranslationData exception" do
         assert_raise I18n::MissingTranslationData do
           I18n.t(:does_not_exist, :default => [:does_not_exist_2, 
:does_not_exist_3], :raise => true)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/i18n/tests/interpolation.rb 
new/lib/i18n/tests/interpolation.rb
--- old/lib/i18n/tests/interpolation.rb 2014-12-19 18:06:15.000000000 +0100
+++ new/lib/i18n/tests/interpolation.rb 2017-02-22 03:51:26.000000000 +0100
@@ -104,6 +104,26 @@
         assert_raise(I18n::ReservedInterpolationKey) { interpolate(:default => 
'%{separator}', :foo => :bar) }
       end
 
+      test "interpolation: deep interpolation for default string" do
+        assert_equal 'Hi %{name}!', interpolate(:default => 'Hi %{name}!', 
:deep_interpolation => true)
+      end
+
+      test "interpolation: deep interpolation for interpolated string" do
+        assert_equal 'Hi Ann!', interpolate(:default => 'Hi %{name}!', :name 
=> 'Ann', :deep_interpolation => true)
+      end
+
+      test "interpolation: deep interpolation for Hash" do
+        people = { :people => { :ann => 'Ann is %{ann}', :john => 'John is 
%{john}' } }
+        interpolated_people = { :people => { :ann => 'Ann is good', :john => 
'John is big' } }
+        assert_equal interpolated_people, interpolate(:default => people, :ann 
=> 'good', :john => 'big', :deep_interpolation => true)
+      end
+
+      test "interpolation: deep interpolation for Array" do
+        people = { :people => ['Ann is %{ann}', 'John is %{john}'] }
+        interpolated_people = { :people => ['Ann is good', 'John is big'] }
+        assert_equal interpolated_people, interpolate(:default => people, :ann 
=> 'good', :john => 'big', :deep_interpolation => true)
+      end
+
       protected
 
       def capture(stream)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/i18n/tests/localization/date.rb 
new/lib/i18n/tests/localization/date.rb
--- old/lib/i18n/tests/localization/date.rb     2014-12-19 18:06:15.000000000 
+0100
+++ new/lib/i18n/tests/localization/date.rb     2017-02-22 03:51:26.000000000 
+0100
@@ -51,6 +51,10 @@
           assert_nothing_raised { I18n.l(@date, options.freeze) }
         end
 
+        test "localize Date: given nil with default value it returns default" 
do
+          assert_equal 'default', I18n.l(nil, :default => 'default')
+        end
+
         test "localize Date: given nil it raises I18n::ArgumentError" do
           assert_raise(I18n::ArgumentError) { I18n.l(nil) }
         end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/i18n/tests/localization/procs.rb 
new/lib/i18n/tests/localization/procs.rb
--- old/lib/i18n/tests/localization/procs.rb    2014-12-19 18:06:15.000000000 
+0100
+++ new/lib/i18n/tests/localization/procs.rb    2017-02-22 03:51:26.000000000 
+0100
@@ -52,19 +52,19 @@
         test "localize Time: given a format that resolves to a Proc it calls 
the Proc with the object" do
           setup_time_proc_translations
           time = ::Time.utc(2008, 3, 1, 6, 0)
-          assert_equal inspect_args([time, {}]), I18n.l(time, :format => 
:proc, :locale => :ru)
+          assert_equal I18n::Tests::Localization::Procs.inspect_args([time, 
{}]), I18n.l(time, :format => :proc, :locale => :ru)
         end
 
         test "localize Time: given a format that resolves to a Proc it calls 
the Proc with the object and extra options" do
           setup_time_proc_translations
           time = ::Time.utc(2008, 3, 1, 6, 0)
           options = { :foo => 'foo' }
-          assert_equal inspect_args([time, options]), I18n.l(time, 
options.merge(:format => :proc, :locale => :ru))
+          assert_equal I18n::Tests::Localization::Procs.inspect_args([time, 
options]), I18n.l(time, options.merge(:format => :proc, :locale => :ru))
         end
 
         protected
 
-          def inspect_args(args)
+          def self.inspect_args(args)
             args = args.map do |arg|
               case arg
               when ::Time, ::DateTime
@@ -85,12 +85,12 @@
             I18n.backend.store_translations :ru, {
               :time => {
                 :formats => {
-                  :proc => lambda { |*args| inspect_args(args) }
+                  :proc => lambda { |*args| 
I18n::Tests::Localization::Procs.inspect_args(args) }
                 }
               },
               :date => {
                 :formats => {
-                  :proc => lambda { |*args| inspect_args(args) }
+                  :proc => lambda { |*args| 
I18n::Tests::Localization::Procs.inspect_args(args) }
                 },
                 :'day_names' => lambda { |key, options|
                   (options[:format] =~ /^%A/) ?
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/i18n/tests/procs.rb new/lib/i18n/tests/procs.rb
--- old/lib/i18n/tests/procs.rb 2014-12-19 18:06:15.000000000 +0100
+++ new/lib/i18n/tests/procs.rb 2017-02-22 03:51:26.000000000 +0100
@@ -4,28 +4,29 @@
   module Tests
     module Procs
       test "lookup: given a translation is a proc it calls the proc with the 
key and interpolation values" do
-        I18n.backend.store_translations(:en, :a_lambda => lambda { |*args| 
filter_args(*args) })
+        I18n.backend.store_translations(:en, :a_lambda => lambda { |*args| 
I18n::Tests::Procs.filter_args(*args) })
         assert_equal '[:a_lambda, {:foo=>"foo"}]', I18n.t(:a_lambda, :foo => 
'foo')
       end
 
       test "defaults: given a default is a Proc it calls it with the key and 
interpolation values" do
-        proc = lambda { |*args| filter_args(*args) }
+        proc = lambda { |*args| I18n::Tests::Procs.filter_args(*args) }
         assert_equal '[nil, {:foo=>"foo"}]', I18n.t(nil, :default => proc, 
:foo => 'foo')
       end
 
       test "defaults: given a default is a key that resolves to a Proc it 
calls it with the key and interpolation values" do
-        I18n.backend.store_translations(:en, :a_lambda => lambda { |*args| 
filter_args(*args) })
+        the_lambda = lambda { |*args| I18n::Tests::Procs.filter_args(*args) }
+        I18n.backend.store_translations(:en, :a_lambda => the_lambda)
         assert_equal '[:a_lambda, {:foo=>"foo"}]', I18n.t(nil, :default => 
:a_lambda, :foo => 'foo')
         assert_equal '[:a_lambda, {:foo=>"foo"}]', I18n.t(nil, :default => 
[nil, :a_lambda], :foo => 'foo')
       end
 
       test "interpolation: given an interpolation value is a lambda it calls 
it with key and values before interpolating it" do
-        proc = lambda { |*args| filter_args(*args) }
+        proc = lambda { |*args| I18n::Tests::Procs.filter_args(*args) }
         assert_match %r(\[\{:foo=>#<Proc.*>\}\]), I18n.t(nil, :default => 
'%{foo}', :foo => proc)
       end
 
       test "interpolation: given a key resolves to a Proc that returns a 
string then interpolation still works" do
-        proc = lambda { |*args| "%{foo}: " + filter_args(*args) }
+        proc = lambda { |*args| "%{foo}: " + 
I18n::Tests::Procs.filter_args(*args) }
         assert_equal 'foo: [nil, {:foo=>"foo"}]', I18n.t(nil, :default => 
proc, :foo => 'foo')
       end
 
@@ -37,17 +38,16 @@
       end
 
       test "lookup: given the option :resolve => false was passed it does not 
resolve proc translations" do
-        I18n.backend.store_translations(:en, :a_lambda => lambda { |*args| 
filter_args(*args) })
+        I18n.backend.store_translations(:en, :a_lambda => lambda { |*args| 
I18n::Tests::Procs.filter_args(*args) })
         assert_equal Proc, I18n.t(:a_lambda, :resolve => false).class
       end
 
       test "lookup: given the option :resolve => false was passed it does not 
resolve proc default" do
-        assert_equal Proc, I18n.t(nil, :default => lambda { |*args| 
filter_args(*args) }, :resolve => false).class
+        assert_equal Proc, I18n.t(nil, :default => lambda { |*args| 
I18n::Tests::Procs.filter_args(*args) }, :resolve => false).class
       end
 
-      protected
 
-      def filter_args(*args)
+      def self.filter_args(*args)
         args.map {|arg| arg.delete(:fallback) if arg.is_a?(Hash) ; arg 
}.inspect
       end
     end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/i18n/version.rb new/lib/i18n/version.rb
--- old/lib/i18n/version.rb     2014-12-19 18:06:15.000000000 +0100
+++ new/lib/i18n/version.rb     2017-02-22 03:51:26.000000000 +0100
@@ -1,3 +1,3 @@
 module I18n
-  VERSION = "0.7.0"
+  VERSION = "0.8.1"
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/i18n.rb new/lib/i18n.rb
--- old/lib/i18n.rb     2014-12-19 18:06:15.000000000 +0100
+++ new/lib/i18n.rb     2017-02-22 03:51:26.000000000 +0100
@@ -9,10 +9,10 @@
   autoload :Locale,  'i18n/locale'
   autoload :Tests,   'i18n/tests'
 
-  RESERVED_KEYS = [:scope, :default, :separator, :resolve, :object, :fallback, 
:format, :cascade, :throw, :raise]
+  RESERVED_KEYS = [:scope, :default, :separator, :resolve, :object, :fallback, 
:format, :cascade, :throw, :raise, :deep_interpolation]
   RESERVED_KEYS_PATTERN = /%\{(#{RESERVED_KEYS.join("|")})\}/
 
-  extend(Module.new {
+  module Base
     # Gets I18n configuration object.
     def config
       Thread.current[:i18n_config] ||= I18n::Config.new
@@ -337,5 +337,7 @@
     def normalized_key_cache
       @normalized_key_cache ||= Hash.new { |h,k| h[k] = {} }
     end
-  })
+  end
+
+  extend Base
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/metadata new/metadata
--- old/metadata        2014-12-19 18:06:15.000000000 +0100
+++ new/metadata        2017-02-22 03:51:26.000000000 +0100
@@ -1,7 +1,7 @@
 --- !ruby/object:Gem::Specification
 name: i18n
 version: !ruby/object:Gem::Version
-  version: 0.7.0
+  version: 0.8.1
 platform: ruby
 authors:
 - Sven Fuchs
@@ -12,7 +12,7 @@
 autorequire: 
 bindir: bin
 cert_chain: []
-date: 2014-12-19 00:00:00.000000000 Z
+date: 2017-02-22 00:00:00.000000000 Z
 dependencies: []
 description: New wave Internationalization support for Ruby.
 email: [email protected]
@@ -23,15 +23,11 @@
 - MIT-LICENSE
 - README.md
 - gemfiles/Gemfile.rails-3.2.x
-- gemfiles/Gemfile.rails-3.2.x.lock
 - gemfiles/Gemfile.rails-4.0.x
-- gemfiles/Gemfile.rails-4.0.x.lock
 - gemfiles/Gemfile.rails-4.1.x
-- gemfiles/Gemfile.rails-4.1.x.lock
 - gemfiles/Gemfile.rails-4.2.x
-- gemfiles/Gemfile.rails-4.2.x.lock
+- gemfiles/Gemfile.rails-5.0.x
 - gemfiles/Gemfile.rails-master
-- gemfiles/Gemfile.rails-master.lock
 - lib/i18n.rb
 - lib/i18n/backend.rb
 - lib/i18n/backend/base.rb
@@ -136,7 +132,7 @@
       version: 1.3.5
 requirements: []
 rubyforge_project: "[none]"
-rubygems_version: 2.4.3
+rubygems_version: 2.6.8
 signing_key: 
 specification_version: 4
 summary: New wave Internationalization support for Ruby
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/test/backend/cache_test.rb 
new/test/backend/cache_test.rb
--- old/test/backend/cache_test.rb      2014-12-19 18:06:15.000000000 +0100
+++ new/test/backend/cache_test.rb      2017-02-22 03:51:26.000000000 +0100
@@ -15,6 +15,7 @@
     I18n.backend = Backend.new
     super
     I18n.cache_store = ActiveSupport::Cache.lookup_store(:memory_store)
+    I18n.cache_key_digest = nil
   end
 
   def teardown
@@ -37,6 +38,12 @@
     assert_equal 'Bar', I18n.t(:bar)
   end
 
+  test "translate returns a cached false response" do
+    I18n.backend.expects(:lookup).never
+    I18n.cache_store.expects(:read).returns(false)
+    assert_equal false, I18n.t(:foo)
+  end
+
   test "still raises MissingTranslationData but also caches it" do
     assert_raise(I18n::MissingTranslationData) { I18n.t(:missing, :raise => 
true) }
     assert_raise(I18n::MissingTranslationData) { I18n.t(:missing, :raise => 
true) }
@@ -60,10 +67,19 @@
 
   test "adds locale and hash of key and hash of options" do
     options = { :bar=>1 }
-    options_hash = I18n::Backend::Cache::USE_INSPECT_HASH ? 
options.inspect.hash : options.hash
+    options_hash = RUBY_VERSION <= "1.9" ? options.inspect.hash : options.hash
     assert_equal "i18n//en/#{:foo.hash}/#{options_hash}", 
I18n.backend.send(:cache_key, :en, :foo, options)
   end
 
+  test "cache_key uses configured digest method" do
+    md5 = Digest::MD5.new
+    options = { :bar=>1 }
+    options_hash = options.inspect
+    with_cache_key_digest(md5) do
+      assert_equal 
"i18n//en/#{md5.hexdigest(:foo.to_s)}/#{md5.hexdigest(options_hash)}", 
I18n.backend.send(:cache_key, :en, :foo, options)
+    end
+  end
+
   test "keys should not be equal" do
     interpolation_values1 = { :foo => 1, :bar => 2 }
     interpolation_values2 = { :foo => 2, :bar => 1 }
@@ -81,6 +97,12 @@
       yield
       I18n.cache_namespace = nil
     end
+
+    def with_cache_key_digest(digest)
+      I18n.cache_key_digest = digest
+      yield
+      I18n.cache_key_digest = nil
+    end
 end
 
 end # AS cache check
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/test/backend/fallbacks_test.rb 
new/test/backend/fallbacks_test.rb
--- old/test/backend/fallbacks_test.rb  2014-12-19 18:06:15.000000000 +0100
+++ new/test/backend/fallbacks_test.rb  2017-02-22 03:51:26.000000000 +0100
@@ -68,6 +68,14 @@
     assert_equal 'Default 6 Bars', I18n.t(:missing_foo, :locale => :'de-DE', 
:default => [:missing_bar, {:other => "Default %{count} Bars"}, "Default Bar"], 
:count => 6)
   end
 
+  test "returns the default translation for a missing :de translation even 
when default is a String when fallback is disabled" do
+    assert_equal 'Default String', I18n.t(:foo, :locale => :de, :default => 
'Default String', :fallback => false)
+  end
+
+  test "raises I18n::MissingTranslationData exception when fallback is 
disabled even when fallback translation exists" do
+    assert_raise(I18n::MissingTranslationData) { I18n.t(:foo, :locale => :de, 
:fallback => false, :raise => true) }
+  end
+
   test "raises I18n::MissingTranslationData exception when no translation was 
found" do
     assert_raise(I18n::MissingTranslationData) { I18n.t(:faa, :locale => :en, 
:raise => true) }
     assert_raise(I18n::MissingTranslationData) { I18n.t(:faa, :locale => :de, 
:raise => true) }
@@ -141,6 +149,10 @@
     assert_equal 'FOO', I18n.t(:foo, :locale => :'de-DE')
   end
 
+  test "falls back from de-DE to de when there is no translation for de-DE 
available when using arrays, too" do
+    assert_equal ['FOO', 'FOO'], I18n.t([:foo, :foo], :locale => :'de-DE')
+  end
+
   test "should not raise error when enforce_available_locales is true, :'pt' 
is missing and default is a Symbol" do
     I18n.enforce_available_locales = true
     begin
@@ -150,3 +162,45 @@
     end
   end
 end
+
+class I18nBackendFallbacksExistsTest < I18n::TestCase
+  class Backend < I18n::Backend::Simple
+    include I18n::Backend::Fallbacks
+  end
+
+  def setup
+    super
+    I18n.backend = Backend.new
+    store_translations(:en, :foo => 'Foo in :en', :bar => 'Bar in :en')
+    store_translations(:de, :bar => 'Bar in :de')
+    store_translations(:'de-DE', :baz => 'Baz in :de-DE')
+  end
+
+  test "exists? given an existing key will return true" do
+    assert_equal true, I18n.exists?(:foo)
+  end
+
+  test "exists? given a non-existing key will return false" do
+    assert_equal false, I18n.exists?(:bogus)
+  end
+
+  test "exists? given an existing key and an existing locale will return true" 
do
+    assert_equal true, I18n.exists?(:foo, :en)
+    assert_equal true, I18n.exists?(:bar, :de)
+  end
+
+  test "exists? given a non-existing key and an existing locale will return 
false" do
+    assert_equal false, I18n.exists?(:bogus, :en)
+    assert_equal false, I18n.exists?(:bogus, :de)
+  end
+
+  test "exists? should return true given a key which is missing from the given 
locale and exists in a fallback locale" do
+    assert_equal true, I18n.exists?(:foo, :de)
+    assert_equal true, I18n.exists?(:foo, :'de-DE')
+  end
+
+  test "exists? should return false given a key which is missing from the 
given locale and all its fallback locales" do
+    assert_equal false, I18n.exists?(:baz, :de)
+    assert_equal false, I18n.exists?(:bogus, :'de-DE')
+  end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/test/core_ext/hash_test.rb 
new/test/core_ext/hash_test.rb
--- old/test/core_ext/hash_test.rb      2014-12-19 18:06:15.000000000 +0100
+++ new/test/core_ext/hash_test.rb      2017-02-22 03:51:26.000000000 +0100
@@ -14,6 +14,18 @@
     assert_equal expected, hash.slice(:foo)
   end
 
+  test "#slice non-existent key" do
+    hash = { :foo => 'bar',  :baz => 'bar' }
+    expected = { :foo => 'bar' }
+    assert_equal expected, hash.slice(:foo, :not_here)
+  end
+
+  test "#slice maintains subclasses of Hash" do
+    klass = Class.new(Hash)
+    hash = klass[:foo, 'bar', :baz, 'bar']
+    assert_instance_of klass,  hash.slice(:foo)
+  end
+
   test "#except" do
     hash = { :foo => 'bar',  :baz => 'bar' }
     expected = { :foo => 'bar' }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/test/gettext/api_test.rb new/test/gettext/api_test.rb
--- old/test/gettext/api_test.rb        2014-12-19 18:06:15.000000000 +0100
+++ new/test/gettext/api_test.rb        2017-02-22 03:51:26.000000000 +0100
@@ -18,6 +18,13 @@
     }, :separator => '|'
   end
 
+  # N_
+  def test_N_returns_original_msg
+    assert_equal 'foo|bar', N_('foo|bar')
+    I18n.locale = :de
+    assert_equal 'Hi Gettext!', N_('Hi Gettext!')
+  end
+
   # gettext
   def test_gettext_uses_msg_as_default
     assert_equal 'Hi Gettext!', _('Hi Gettext!')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/test/i18n/exceptions_test.rb 
new/test/i18n/exceptions_test.rb
--- old/test/i18n/exceptions_test.rb    2014-12-19 18:06:15.000000000 +0100
+++ new/test/i18n/exceptions_test.rb    2017-02-22 03:51:26.000000000 +0100
@@ -13,6 +13,11 @@
     end
   end
 
+  test "MissingTranslation can be initialized without options" do
+    exception = I18n::MissingTranslation.new(:en, 'foo')
+    assert_equal({}, exception.options)
+  end
+
   test "MissingTranslationData exception stores locale, key and options" do
     force_missing_translation_data do |exception|
       assert_equal 'de', exception.locale
@@ -66,6 +71,10 @@
       assert_equal 'reserved key :scope used in "%{scope}"', exception.message
     end
   end
+  
+  test "MissingTranslationData#new can be initialized with just two arguments" 
do
+    assert I18n::MissingTranslationData.new('en', 'key')
+  end
 
   private
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/test/i18n/interpolate_test.rb 
new/test/i18n/interpolate_test.rb
--- old/test/i18n/interpolate_test.rb   2014-12-19 18:06:15.000000000 +0100
+++ new/test/i18n/interpolate_test.rb   2017-02-22 03:51:26.000000000 +0100
@@ -57,6 +57,17 @@
   def test_sprintf_mix_unformatted_and_formatted_named_placeholders
     assert_equal "foo 1.000000", I18n.interpolate("%{name} %<num>f", :name => 
"foo", :num => 1.0)
   end
+
+  class RailsSafeBuffer < String
+
+    def gsub(*args, &block)
+      to_str.gsub(*args, &block)
+    end
+
+  end
+  test "with String subclass that redefined gsub method" do
+    assert_equal "Hello mars world", 
I18n.interpolate(RailsSafeBuffer.new("Hello %{planet} world"), :planet => 
'mars') 
+  end
 end
 
 class I18nMissingInterpolationCustomHandlerTest < I18n::TestCase
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/test/i18n_test.rb new/test/i18n_test.rb
--- old/test/i18n_test.rb       2014-12-19 18:06:15.000000000 +0100
+++ new/test/i18n_test.rb       2017-02-22 03:51:26.000000000 +0100
@@ -269,6 +269,10 @@
     assert_raise(I18n::ArgumentError) { I18n.l nil }
   end
 
+  test "localize given nil and default returns default" do
+    assert_nil I18n.l(nil, :default => nil)
+  end
+
   test "localize given an Object raises an I18n::ArgumentError" do
     assert_raise(I18n::ArgumentError) { I18n.l Object.new }
   end
@@ -347,6 +351,10 @@
     end
   end
 
+  test "transliterate non-ASCII chars not in map with default replacement 
char" do
+    assert_equal "???", I18n.transliterate("日本語")
+  end
+
   test "I18n.locale_available? returns true when the passed locale is 
available" do
     I18n.available_locales = [:en, :de]
     assert_equal true, I18n.locale_available?(:de)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/test/test_data/locales/plurals.rb 
new/test/test_data/locales/plurals.rb
--- old/test/test_data/locales/plurals.rb       2014-12-19 18:06:15.000000000 
+0100
+++ new/test/test_data/locales/plurals.rb       2017-02-22 03:51:26.000000000 
+0100
@@ -74,7 +74,7 @@
   :or => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { 
|n| n == 1 ? :one : :other } } } },
   :pa => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { 
|n| n == 1 ? :one : :other } } } },
   :pap => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { 
|n| n == 1 ? :one : :other } } } },
-  :pl => { :i18n => { :plural => { :keys => [:one, :few, :other], :rule => 
lambda { |n| n == 1 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 
14].include?(n % 100) ? :few : :other } } } },
+  :pl => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule 
=> lambda { |n| n == 1 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 
14].include?(n % 100) ? :few : (n != 1 && [0, 1].include?(n % 10)) || [5, 6, 7, 
8, 9].include?(n % 10) || [12, 13, 14].include?(n % 100) ? :many : :other } } } 
},
   :ps => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { 
|n| n == 1 ? :one : :other } } } },
   :pt => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { 
|n| [0, 1].include?(n) ? :one : :other } } } },
   :"pt-PT" => { :i18n => { :plural => { :keys => [:one, :other], :rule => 
lambda { |n| n == 1 ? :one : :other } } } },


Reply via email to