i18n 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of i18n might be problematic. Click here for more details.

Files changed (77) hide show
  1. data/README.textile +44 -9
  2. data/Rakefile +2 -1
  3. data/VERSION +1 -1
  4. data/lib/i18n.rb +60 -15
  5. data/lib/i18n/backend.rb +14 -0
  6. data/lib/i18n/backend/active_record.rb +69 -0
  7. data/lib/i18n/backend/active_record/store_procs.rb +37 -0
  8. data/lib/i18n/backend/active_record/translation.rb +82 -0
  9. data/lib/i18n/backend/active_record_missing.rb +55 -0
  10. data/lib/i18n/backend/base.rb +235 -0
  11. data/lib/i18n/backend/cache.rb +71 -0
  12. data/lib/i18n/backend/chain.rb +74 -0
  13. data/lib/i18n/backend/fallbacks.rb +51 -0
  14. data/lib/i18n/backend/gettext.rb +75 -0
  15. data/lib/i18n/backend/helpers.rb +53 -0
  16. data/lib/i18n/backend/metadata.rb +73 -0
  17. data/lib/i18n/backend/pluralization.rb +57 -0
  18. data/lib/i18n/backend/simple.rb +15 -227
  19. data/lib/i18n/core_ext/object/meta_class.rb +5 -0
  20. data/lib/i18n/{string.rb → core_ext/string/interpolate.rb} +2 -0
  21. data/lib/i18n/exceptions.rb +2 -0
  22. data/lib/i18n/gettext.rb +25 -0
  23. data/lib/i18n/helpers.rb +5 -0
  24. data/lib/i18n/helpers/gettext.rb +64 -0
  25. data/lib/i18n/locale.rb +6 -0
  26. data/lib/i18n/locale/fallbacks.rb +98 -0
  27. data/lib/i18n/locale/tag.rb +28 -0
  28. data/lib/i18n/locale/tag/parents.rb +24 -0
  29. data/lib/i18n/locale/tag/rfc4646.rb +76 -0
  30. data/lib/i18n/locale/tag/simple.rb +41 -0
  31. data/test/all.rb +7 -2
  32. data/test/api/basics.rb +3 -1
  33. data/test/api/interpolation.rb +35 -4
  34. data/test/api/lambda.rb +5 -3
  35. data/test/api/link.rb +4 -2
  36. data/test/api/localization/date.rb +2 -0
  37. data/test/api/localization/date_time.rb +3 -1
  38. data/test/api/localization/lambda.rb +4 -2
  39. data/test/api/localization/time.rb +3 -1
  40. data/test/api/pluralization.rb +12 -15
  41. data/test/api/translation.rb +5 -3
  42. data/test/backend/active_record/active_record_test.rb +40 -0
  43. data/test/backend/active_record/all.rb +3 -0
  44. data/test/backend/active_record/api_test.rb +54 -0
  45. data/test/backend/active_record/setup.rb +166 -0
  46. data/test/backend/active_record_missing/active_record_missing_test.rb +63 -0
  47. data/test/backend/all/api_test.rb +88 -0
  48. data/test/backend/cache/cache_test.rb +69 -0
  49. data/test/backend/chain/api_test.rb +80 -0
  50. data/test/backend/chain/chain_test.rb +64 -0
  51. data/test/backend/fallbacks/api_test.rb +84 -0
  52. data/test/backend/fallbacks/fallbacks_test.rb +57 -0
  53. data/test/backend/metadata/metadata_test.rb +65 -0
  54. data/test/backend/pluralization/api_test.rb +86 -0
  55. data/test/backend/pluralization/pluralization_test.rb +43 -0
  56. data/test/backend/simple/all.rb +2 -0
  57. data/test/backend/simple/api_test.rb +27 -20
  58. data/test/backend/simple/helpers_test.rb +26 -0
  59. data/test/backend/simple/lookup_test.rb +2 -1
  60. data/test/backend/simple/{setup/localization.rb → setup.rb} +29 -11
  61. data/test/backend/simple/translations_test.rb +1 -6
  62. data/test/{string_test.rb → core_ext/string/interpolate_test.rb} +4 -2
  63. data/test/fixtures/locales/de.po +67 -0
  64. data/test/fixtures/locales/en.rb +2 -0
  65. data/test/fixtures/locales/plurals.rb +113 -0
  66. data/test/gettext/api_test.rb +204 -0
  67. data/test/gettext/backend_test.rb +84 -0
  68. data/test/i18n_exceptions_test.rb +3 -1
  69. data/test/i18n_load_path_test.rb +8 -1
  70. data/test/i18n_test.rb +30 -7
  71. data/test/locale/fallbacks_test.rb +128 -0
  72. data/test/locale/tag/rfc4646_test.rb +145 -0
  73. data/test/locale/tag/simple_test.rb +35 -0
  74. data/test/test_helper.rb +11 -5
  75. data/test/with_options.rb +2 -0
  76. metadata +75 -11
  77. data/test/backend/simple/setup/base.rb +0 -21
@@ -1,20 +1,55 @@
1
- h1. Ruby I18n gem
1
+ h1. Ruby I18n
2
2
 
3
- I18n and localization solution for Ruby.
3
+ Ruby Internationalization and localization solution.
4
4
 
5
- For information please refer to http://rails-i18n.org
5
+ Features:
6
+
7
+ * translation and localization
8
+ * interpolation of values to translations (Ruby 1.9 compatible syntax)
9
+ * pluralization (CLDR compatible)
10
+ * flexible defaults
11
+ * bulk lookup
12
+ * lambdas as translation data
13
+ * custom key/scope separator
14
+ * custom exception handlers
15
+ * extensible architecture with a swappable backend
16
+
17
+ Experimental, pluggable features:
18
+
19
+ * lambda pluralizers stored as translation data
20
+ * RFC4647 compliant locale fallbacks (with optional RFC4646 locale validation)
21
+ * backend cache
22
+
23
+ For more information and lots of resources see: "http://rails-i18n.org/wiki":http://rails-i18n.org/wiki
24
+
25
+ h2. Install
26
+
27
+ gem install i18n
28
+
29
+ h3. Install on Rails 2.3.4 (deprecated)
30
+
31
+ Rails 2.3.4 will not accept i18n gems > 0.1.3. There is an unpacked gem inside of active_support/lib/vendor which gets loaded unless gem 'i18n', '~> 0.1.3'. This requirement is relaxed in http://github.com/rails/rails/commit/6da036538334fd459156ab2789c9fae5512be5d2
32
+
33
+ The following is needed to get the new i18n gem inside vendor/plugins loaded properly.
34
+
35
+ def reload_i18n!
36
+ raise "Move to i18n version 0.2.0 or greater" if Rails.version > "2.3.4"
37
+
38
+ $:.grep(/i18n/).each { |path| $:.delete(path) }
39
+ I18n::Backend.send :remove_const, "Simple"
40
+ $: << Rails.root.join('vendor', 'plugins', 'i18n', 'lib').to_s
41
+ end
42
+
43
+ Then you can `reload_i18n!` inside an i18n initializer.
6
44
 
7
45
  h2. Authors
8
46
 
9
- * "Matt Aimonetti":http://railsontherun.com
10
47
  * "Sven Fuchs":http://www.artweb-design.de
11
48
  * "Joshua Harvey":http://www.workingwithrails.com/person/759-joshua-harvey
12
- * "Saimon Moore":http://saimonmoore.net
13
49
  * "Stephan Soller":http://www.arkanis-development.de
50
+ * "Saimon Moore":http://saimonmoore.net
51
+ * "Matt Aimonetti":http://railsontherun.com
14
52
 
15
53
  h2. License
16
54
 
17
- MIT License. See the included MIT-LICENCE file.
18
-
19
-
20
-
55
+ MIT License. See the included MIT-LICENCE file.
data/Rakefile CHANGED
@@ -8,6 +8,7 @@ begin
8
8
  require 'jeweler'
9
9
  Jeweler::Tasks.new do |s|
10
10
  s.name = "i18n"
11
+ s.rubyforge_project = "i18n"
11
12
  s.summary = "New wave Internationalization support for Ruby"
12
13
  s.email = "rails-i18n@googlegroups.com"
13
14
  s.homepage = "http://rails-i18n.org"
@@ -17,4 +18,4 @@ begin
17
18
  end
18
19
  rescue LoadError
19
20
  puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
20
- end
21
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.1
1
+ 0.3.0
@@ -1,18 +1,23 @@
1
- # Authors:: Matt Aimonetti (http://railsontherun.com/),
2
- # Sven Fuchs (http://www.artweb-design.de),
1
+ # encoding: utf-8
2
+
3
+ # Authors:: Sven Fuchs (http://www.artweb-design.de),
3
4
  # Joshua Harvey (http://www.workingwithrails.com/person/759-joshua-harvey),
5
+ # Stephan Soller (http://www.arkanis-development.de/),
4
6
  # Saimon Moore (http://saimonmoore.net),
5
- # Stephan Soller (http://www.arkanis-development.de/)
7
+ # Matt Aimonetti (http://railsontherun.com/)
6
8
  # Copyright:: Copyright (c) 2008 The Ruby i18n Team
7
9
  # License:: MIT
8
- require 'i18n/backend/simple'
9
10
  require 'i18n/exceptions'
10
- require 'i18n/string'
11
+ require 'i18n/core_ext/string/interpolate'
11
12
 
12
13
  module I18n
14
+ autoload :Backend, 'i18n/backend'
15
+ autoload :Helpers, 'i18n/helpers'
16
+ autoload :Locale, 'i18n/locale'
17
+
13
18
  @@backend = nil
14
19
  @@load_path = nil
15
- @@default_locale = :'en'
20
+ @@default_locale = :en
16
21
  @@default_separator = '.'
17
22
  @@exception_handler = :default_exception_handler
18
23
 
@@ -34,7 +39,7 @@ module I18n
34
39
 
35
40
  # Sets the current default locale. Used to set a custom default locale.
36
41
  def default_locale=(locale)
37
- @@default_locale = locale
42
+ @@default_locale = locale.to_sym rescue nil
38
43
  end
39
44
 
40
45
  # Returns the current locale. Defaults to I18n.default_locale.
@@ -44,12 +49,19 @@ module I18n
44
49
 
45
50
  # Sets the current locale pseudo-globally, i.e. in the Thread.current hash.
46
51
  def locale=(locale)
47
- Thread.current[:locale] = locale
52
+ Thread.current[:locale] = locale.to_sym rescue nil
48
53
  end
49
54
 
50
- # Returns an array of locales for which translations are available
55
+ # Returns an array of locales for which translations are available.
56
+ # Unless you explicitely set the these through I18n.available_locales=
57
+ # the call will be delegated to the backend and memoized on the I18n module.
51
58
  def available_locales
52
- backend.available_locales
59
+ @@available_locales ||= backend.available_locales
60
+ end
61
+
62
+ # Sets the available locales.
63
+ def available_locales=(locales)
64
+ @@available_locales = locales
53
65
  end
54
66
 
55
67
  # Returns the current default scope separator. Defaults to '.'
@@ -176,13 +188,13 @@ module I18n
176
188
  # *LAMBDAS*
177
189
  #
178
190
  # Both translations and defaults can be given as Ruby lambdas. Lambdas will be
179
- # called and passed the key and options.
191
+ # called and passed the key and options.
180
192
  #
181
193
  # E.g. assuming the key <tt>:salutation</tt> resolves to:
182
194
  # lambda { |key, options| options[:gender] == 'm' ? "Mr. {{options[:name]}}" : "Mrs. {{options[:name]}}" }
183
195
  #
184
196
  # Then <tt>I18n.t(:salutation, :gender => 'w', :name => 'Smith') will result in "Mrs. Smith".
185
- #
197
+ #
186
198
  # It is recommended to use/implement lambdas in an "idempotent" way. E.g. when
187
199
  # a cache layer is put in front of I18n.translate it will generate a cache key
188
200
  # from the argument values passed to #translate. Therefor your lambdas should
@@ -192,13 +204,19 @@ module I18n
192
204
  options = args.last.is_a?(Hash) ? args.pop : {}
193
205
  key = args.shift
194
206
  locale = options.delete(:locale) || I18n.locale
207
+ raises = options.delete(:raise)
195
208
  backend.translate(locale, key, options)
196
- rescue I18n::ArgumentError => e
197
- raise e if options[:raise]
198
- send(@@exception_handler, e, locale, key, options)
209
+ rescue I18n::ArgumentError => exception
210
+ raise exception if raises
211
+ handle_exception(exception, locale, key, options)
199
212
  end
200
213
  alias :t :translate
201
214
 
215
+ def translate!(key, options = {})
216
+ translate(key, options.merge( :raise => true ))
217
+ end
218
+ alias :t! :translate!
219
+
202
220
  # Localizes certain objects, such as dates and numbers to local formatting.
203
221
  def localize(object, options = {})
204
222
  locale = options[:locale] || I18n.locale
@@ -208,6 +226,7 @@ module I18n
208
226
  alias :l :localize
209
227
 
210
228
  protected
229
+
211
230
  # Handles exceptions raised in the backend. All exceptions except for
212
231
  # MissingTranslationData exceptions are re-raised. When a MissingTranslationData
213
232
  # was caught and the option :raise is not set the handler returns an error
@@ -217,6 +236,32 @@ module I18n
217
236
  raise exception
218
237
  end
219
238
 
239
+ # Any exceptions thrown in translate will be sent to the @@exception_handler
240
+ # which can be a Symbol, a Proc or any other Object.
241
+ #
242
+ # If exception_handler is a Symbol then it will simply be sent to I18n as
243
+ # a method call. A Proc will simply be called. In any other case the
244
+ # method #call will be called on the exception_handler object.
245
+ #
246
+ # Examples:
247
+ #
248
+ # I18n.exception_handler = :default_exception_handler # this is the default
249
+ # I18n.default_exception_handler(exception, locale, key, options) # will be called like this
250
+ #
251
+ # I18n.exception_handler = lambda { |*args| ... } # a lambda
252
+ # I18n.exception_handler.call(exception, locale, key, options) # will be called like this
253
+ #
254
+ # I18n.exception_handler = I18nExceptionHandler.new # an object
255
+ # I18n.exception_handler.call(exception, locale, key, options) # will be called like this
256
+ def handle_exception(exception, locale, key, options)
257
+ case @@exception_handler
258
+ when Symbol
259
+ send(@@exception_handler, exception, locale, key, options)
260
+ else
261
+ @@exception_handler.call(exception, locale, key, options)
262
+ end
263
+ end
264
+
220
265
  # Merges the given locale, key and scope into a single array of keys.
221
266
  # Splits keys that contain dots into multiple keys. Makes sure all
222
267
  # keys are Symbols.
@@ -0,0 +1,14 @@
1
+ module I18n
2
+ module Backend
3
+ autoload :ActiveRecord, 'i18n/backend/active_record'
4
+ autoload :Base, 'i18n/backend/base'
5
+ autoload :Cache, 'i18n/backend/cache'
6
+ autoload :Chain, 'i18n/backend/chain'
7
+ autoload :Fallbacks, 'i18n/backend/fallbacks'
8
+ autoload :Gettext, 'i18n/backend/gettext'
9
+ autoload :Helpers, 'i18n/backend/helpers'
10
+ autoload :Metadata, 'i18n/backend/metadata'
11
+ autoload :Pluralization, 'i18n/backend/pluralization'
12
+ autoload :Simple, 'i18n/backend/simple'
13
+ end
14
+ end
@@ -0,0 +1,69 @@
1
+ require 'i18n/backend/base'
2
+ require 'i18n/backend/active_record/translation'
3
+
4
+ #
5
+ # This backend reads translations from a Translations table in environment database. Note that the database
6
+ # will not automatically be prepopulated with missing keys. You can achieve this effect with the ActiveRecordMissing backend,
7
+ # as the following example shows:
8
+ #
9
+ # I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRecord.new, I18.backend, I18n::Backend::ActiveRecordMissing.new)
10
+ #
11
+ module I18n
12
+ module Backend
13
+ class ActiveRecord
14
+ autoload :StoreProcs, 'i18n/backend/active_record/store_procs'
15
+ autoload :Translation, 'i18n/backend/active_record/translation'
16
+
17
+ include Base
18
+
19
+ def reload!
20
+ end
21
+
22
+ def store_translations(locale, data)
23
+ separator = I18n.default_separator # TODO allow to pass as an option?
24
+ wind_keys(data).each do |key, v|
25
+ Translation.locale(locale).lookup(expand_keys(key, separator), separator).delete_all
26
+ Translation.create(:locale => locale.to_s, :key => key, :value => v)
27
+ end
28
+ end
29
+
30
+ def available_locales
31
+ begin
32
+ Translation.available_locales
33
+ rescue ::ActiveRecord::StatementInvalid
34
+ []
35
+ end
36
+ end
37
+
38
+ protected
39
+
40
+ def lookup(locale, key, scope = [], separator = nil)
41
+ return unless key
42
+
43
+ separator ||= I18n.default_separator
44
+ key = (Array(scope) + Array(key)).join(separator)
45
+
46
+ result = Translation.locale(locale).lookup(key, separator).all
47
+ if result.empty?
48
+ return nil
49
+ elsif result.first.key == key
50
+ return result.first.value
51
+ else
52
+ chop_range = (key.size + separator.size)..-1
53
+ result = result.inject({}) do |hash, r|
54
+ hash[r.key.slice(chop_range)] = r.value
55
+ hash
56
+ end
57
+ deep_symbolize_keys(unwind_keys(result))
58
+ end
59
+ end
60
+
61
+ # For a key :'foo.bar.baz' return ['foo', 'foo.bar', 'foo.bar.baz']
62
+ def expand_keys(key, separator = I18n.default_separator)
63
+ key.to_s.split(separator).inject([]) do |keys, key|
64
+ keys << [keys.last, key].compact.join(separator)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,37 @@
1
+ # This module is intended to be mixed into the ActiveRecord backend to allow
2
+ # storing Ruby Procs as translation values in the database.
3
+ #
4
+ # I18n.backend = I18n::Backend::ActiveRecord.new
5
+ # I18n::Backend::ActiveRecord::Translation.send(:include, I18n::Backend::ActiveRecord::StoreProcs)
6
+ #
7
+ # The StoreProcs module requires the ParseTree and ruby2ruby gems and therefor
8
+ # was extracted from the original backend.
9
+ #
10
+ # ParseTree is not compatible with Ruby 1.9.
11
+ module I18n
12
+ module Backend
13
+ class ActiveRecord
14
+ module StoreProcs
15
+ unless RUBY_VERSION >= '1.9'
16
+ class << self
17
+ def included(target)
18
+ require 'ruby2ruby'
19
+ require 'parse_tree'
20
+ require 'parse_tree_extensions'
21
+ end
22
+ end
23
+
24
+ def value=(v)
25
+ case v
26
+ when Proc
27
+ write_attribute(:value, v.to_ruby)
28
+ write_attribute(:is_proc, true)
29
+ else
30
+ write_attribute(:value, v)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,82 @@
1
+ require 'active_record'
2
+
3
+ module I18n
4
+ module Backend
5
+ # ActiveRecord model used to store actual translations to the database.
6
+ #
7
+ # This model expects a table like the following to be already set up in
8
+ # your the database:
9
+ #
10
+ # create_table :translations do |t|
11
+ # t.string :locale
12
+ # t.string :key
13
+ # t.string :value
14
+ # t.boolean :is_proc, :default => false
15
+ # end
16
+ #
17
+ # This model supports to named scopes :locale and :lookup. The :locale
18
+ # scope simply adds a condition for a given locale:
19
+ #
20
+ # I18n::Backend::ActiveRecord::Translation.locale(:en).all
21
+ # # => all translation records that belong to the :en locale
22
+ #
23
+ # The :lookup scope adds a condition for looking up all translations
24
+ # that either start with the given keys (joined by an optionally given
25
+ # separator or I18n.default_separator) or that exactly have this key.
26
+ #
27
+ # # with translations present for :"foo.bar" and :"foo.baz"
28
+ # I18n::Backend::ActiveRecord::Translation.lookup(:foo)
29
+ # # => an array with both translation records :"foo.bar" and :"foo.baz"
30
+ #
31
+ # I18n::Backend::ActiveRecord::Translation.lookup([:foo, :bar])
32
+ # I18n::Backend::ActiveRecord::Translation.lookup(:"foo.bar")
33
+ # # => an array with the translation record :"foo.bar"
34
+ #
35
+ # When the StoreProcs module was mixed into this model then Procs will
36
+ # be stored to the database as Ruby code and evaluated when :value is
37
+ # called.
38
+ #
39
+ # Translation = I18n::Backend::ActiveRecord::Translation
40
+ # Translation.create \
41
+ # :locale => 'en'
42
+ # :key => 'foo'
43
+ # :value => lambda { |key, options| 'FOO' }
44
+ # Translation.find_by_locale_and_key('en', 'foo').value
45
+ # # => 'FOO'
46
+ class ActiveRecord
47
+ class Translation < ::ActiveRecord::Base
48
+ set_table_name 'translations'
49
+ attr_protected :is_proc, :interpolations
50
+
51
+ serialize :value
52
+ serialize :interpolations, Array
53
+
54
+ named_scope :locale, lambda { |locale|
55
+ { :conditions => { :locale => locale.to_s } }
56
+ }
57
+
58
+ named_scope :lookup, lambda { |keys, *separator|
59
+ keys = Array(keys).map! { |key| key.to_s }
60
+ separator ||= I18n.default_separator
61
+ { :conditions => ["`key` IN (?) OR `key` LIKE '#{keys.last}#{separator}%'", keys] }
62
+ }
63
+
64
+ def self.available_locales
65
+ Translation.find(:all, :select => 'DISTINCT locale').map { |t| t.locale }
66
+ end
67
+
68
+ def interpolates?(key)
69
+ self.interpolations.include?(key) if self.interpolations
70
+ end
71
+
72
+ def value
73
+ if is_proc
74
+ Kernel.eval read_attribute(:value)
75
+ else
76
+ read_attribute(:value)
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,55 @@
1
+ #
2
+ # This extension stores untranslated keys as translation stubs in the database. This is useful if you have a web based
3
+ # translation tool. It will populate with untranslated keys as the application is being used. A translator can then go
4
+ # through these. Example usage:
5
+ #
6
+ # I18n::Backend::Chain.send(:include, I18n::Backend::ActiveRecordMissing)
7
+ # I18n.backend = I18nChainBackend.new(I18n::Backend::ActiveRecord.new, I18n::Backend::Simple.new)
8
+ #
9
+ # Stubs for pluralizations will also be created for each key defined in i18n.plural_keys. Eg:
10
+ #
11
+ # en:
12
+ # i18n:
13
+ # plural_keys: [:zero, :one, :other]
14
+ #
15
+ # pl:
16
+ # i18n:
17
+ # plural_keys: [:zero, :one, :few, :other]
18
+ #
19
+ # This extension can also persist interpolation keys in Translation#interpolations. This is useful for translators
20
+ # who cannot infer possible interpolations from the keys, as they can with other solutions such as gettext or globalize.
21
+ #
22
+ module I18n
23
+ module Backend
24
+ module ActiveRecordMissing
25
+
26
+ def store_default_translations(locale, key, options = {})
27
+ count, scope, default, separator = options.values_at(:count, *Base::RESERVED_KEYS)
28
+ separator ||= I18n.default_separator
29
+ keys = I18n.send(:normalize_translation_keys, locale, key, scope, separator)[1..-1]
30
+ key = keys.join(separator || I18n.default_separator)
31
+
32
+ unless ActiveRecord::Translation.locale(locale).lookup(key, separator).exists?
33
+ interpolations = options.reject { |name, value| Base::RESERVED_KEYS.include?(name) }.keys
34
+ keys = count ? I18n.t('i18n.plural_keys', :locale => locale).map { |k| [key, k].join(separator) } : [key]
35
+ keys.each { |key| store_default_translation(locale, key, interpolations) }
36
+ end
37
+ end
38
+
39
+ def store_default_translation(locale, key, interpolations)
40
+ translation = ActiveRecord::Translation.new :locale => locale.to_s, :key => key
41
+ translation.interpolations = interpolations
42
+ translation.save
43
+ end
44
+
45
+ def translate(locale, key, options = {})
46
+ super
47
+
48
+ rescue I18n::MissingTranslationData => e
49
+ self.store_default_translations(locale, key, options)
50
+
51
+ raise e
52
+ end
53
+ end
54
+ end
55
+ end