Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add flexibility to exception handling (deprectates I18n.default_excep…

…tion_handler).

Add an I18n::ExceptionHandler class to allow the inclusion of modules for more flexiblity in handling exceptions. 

Also, include the ability to html format the message of MissingTranslationData similar to what ActionPack's view translation helper does (so it can be removed or bypassed over there, giving more control to users).

Also, add the ability to pass an :exception_handler option to force usage of an exception_handler per request.

Also, deprecate I18n.default_exception_handler. Please use the class I18n::ExceptionHandler instead (an instance of which is set to I18n.exception_handler by default)
  • Loading branch information...
commit 2913ff9a7544f223f60e7d7b32c2a0e1af89812b 1 parent 68fdfe4
@svenfuchs authored
View
36 lib/i18n.rb
@@ -253,7 +253,6 @@ def with_locale(tmp_locale = nil)
self.locale = current_locale if tmp_locale
end
-
# Merges the given locale, key and scope into a single array of keys.
# Splits keys that contain dots into multiple keys. Makes sure all
# keys are Symbols.
@@ -271,15 +270,6 @@ def normalize_keys(locale, key, scope, separator = nil)
# see http://redmine.ruby-lang.org/repositories/revision/ruby-19?rev=24280
private
- # Handles exceptions raised in the backend. All exceptions except for
- # MissingTranslationData exceptions are re-raised. When a MissingTranslationData
- # was caught and the option :raise is not set the handler returns an error
- # message string containing the key/scope.
- def default_exception_handler(exception, locale, key, options)
- return exception.message if MissingTranslationData === exception
- raise exception
- end
-
# Any exceptions thrown in translate will be sent to the @@exception_handler
# which can be a Symbol, a Proc or any other Object.
#
@@ -298,20 +288,14 @@ def default_exception_handler(exception, locale, key, options)
# I18n.exception_handler = I18nExceptionHandler.new # an object
# I18n.exception_handler.call(exception, locale, key, options) # will be called like this
def handle_exception(exception, locale, key, options)
- case config.exception_handler
+ case handler = options[:exception_handler] || config.exception_handler
when Symbol
- send(config.exception_handler, exception, locale, key, options)
+ send(handler, exception, locale, key, options)
else
- config.exception_handler.call(exception, locale, key, options)
+ handler.call(exception, locale, key, options)
end
end
- # Deprecated. Will raise a warning in future versions and then finally be
- # removed. Use I18n.normalize_keys instead.
- def normalize_translation_keys(locale, key, scope, separator = nil)
- normalize_keys(locale, key, scope, separator)
- end
-
def normalize_key(key, separator)
normalized_key_cache[separator][key] ||=
case key
@@ -328,5 +312,19 @@ def normalize_key(key, separator)
def normalized_key_cache
@normalized_key_cache ||= Hash.new { |h,k| h[k] = {} }
end
+
+ # DEPRECATED. Use I18n.normalize_keys instead.
+ def normalize_translation_keys(locale, key, scope, separator = nil)
+ puts "I18n.normalize_translation_keys is deprecated. Please use the class I18n.normalize_keys instead."
+ normalize_keys(locale, key, scope, separator)
+ end
+
+ # DEPRECATED. Please use the I18n::ExceptionHandler class instead.
+ def default_exception_handler(exception, locale, key, options)
+ puts "I18n.default_exception_handler is deprecated. Please use the class I18n::ExceptionHandler instead " +
+ "(an instance of which is set to I18n.exception_handler by default)."
+ return exception.message if MissingTranslationData === exception
+ raise exception
+ end
end
end
View
4 lib/i18n/config.rb
@@ -57,7 +57,7 @@ def default_separator=(separator)
# Return the current exception handler. Defaults to :default_exception_handler.
def exception_handler
- @@exception_handler ||= :default_exception_handler
+ @@exception_handler ||= ExceptionHandler.new
end
# Sets the exception handler.
@@ -83,4 +83,4 @@ def load_path=(load_path)
@@load_path = load_path
end
end
-end
+end
View
28 lib/i18n/exceptions.rb
@@ -7,6 +7,22 @@ def initialize(message = nil)
end unless defined?(KeyError)
module I18n
+ # Handles exceptions raised in the backend. All exceptions except for
+ # MissingTranslationData exceptions are re-raised. When a MissingTranslationData
+ # was caught the handler returns an error message string containing the key/scope.
+ # Note that the exception handler is not called when the option :raise was given.
+ class ExceptionHandler
+ include Module.new {
+ def call(exception, locale, key, options)
+ if exception.is_a?(MissingTranslationData)
+ options[:rescue_format] == :html ? exception.html_message : exception.message
+ else
+ raise exception
+ end
+ end
+ }
+ end
+
class ArgumentError < ::ArgumentError; end
class InvalidLocale < ArgumentError
@@ -27,16 +43,22 @@ def initialize(filename)
class MissingTranslationData < ArgumentError
attr_reader :locale, :key, :options
+
def initialize(locale, key, opts = nil)
@key, @locale, @options = key, locale, opts.dup || {}
options.each { |k, v| options[k] = v.inspect if v.is_a?(Proc) }
super "translation missing: #{keys.join('.')}"
end
+ def html_message
+ key = keys.last.to_s.gsub('_', ' ').gsub(/\b('?[a-z])/) { $1.capitalize }
+ %(<span class="translation_missing">#{key}</span>)
+ end
+
def keys
- keys = I18n.normalize_keys(locale, key, options[:scope])
- keys << 'no key' if keys.size < 2
- keys
+ @keys ||= I18n.normalize_keys(locale, key, options[:scope]).tap do |keys|
+ keys << 'no key' if keys.size < 2
+ end
end
end
View
34 test/i18n_exceptions_test.rb
@@ -30,6 +30,17 @@ def test_missing_translation_data_message
assert_equal 'translation missing: de.bar.foo', e.message
end
+ def test_missing_translation_data_html_message
+ force_missing_translation_data
+ rescue I18n::ArgumentError => e
+ assert_equal '<span class="translation_missing">Foo</span>', e.html_message
+ end
+
+ def test_missing_translation_data_html_message
+ message = force_missing_translation_data(:rescue_format => :html)
+ assert_equal '<span class="translation_missing">Foo</span>', message
+ end
+
def test_invalid_pluralization_data_stores_entry_and_count
force_invalid_pluralization_data
rescue I18n::ArgumentError => e
@@ -44,7 +55,7 @@ def test_invalid_pluralization_data_message
end
def test_missing_interpolation_argument_stores_key_and_string
- assert_raise(I18n::MissingInterpolationArgument) { force_missing_interpolation_argument }
+ assert_raise(I18n::MissingInterpolationArgument) { force_missing_interpolation_argument }
force_missing_interpolation_argument
rescue I18n::ArgumentError => e
# assert_equal :bar, e.key
@@ -71,27 +82,28 @@ def test_reserved_interpolation_key_message
end
private
+
def force_invalid_locale
- I18n.backend.translate nil, :foo
+ I18n.translate(:foo, :locale => nil)
end
- def force_missing_translation_data
- I18n.backend.store_translations 'de', :bar => nil
- I18n.backend.translate 'de', :foo, :scope => :bar
+ def force_missing_translation_data(options = {})
+ I18n.backend.store_translations('de', :bar => nil)
+ I18n.translate(:foo, options.merge(:scope => :bar, :locale => :de))
end
def force_invalid_pluralization_data
- I18n.backend.store_translations 'de', :foo => [:bar]
- I18n.backend.translate 'de', :foo, :count => 1
+ I18n.backend.store_translations('de', :foo => [:bar])
+ I18n.translate(:foo, :count => 1, :locale => :de)
end
def force_missing_interpolation_argument
- I18n.backend.store_translations 'de', :foo => "%{bar}"
- I18n.backend.translate 'de', :foo, :baz => 'baz'
+ I18n.backend.store_translations('de', :foo => "%{bar}")
+ I18n.translate(:foo, :baz => 'baz', :locale => :de)
end
def force_reserved_interpolation_key
- I18n.backend.store_translations 'de', :foo => "%{scope}"
- I18n.backend.translate 'de', :foo, :baz => 'baz'
+ I18n.backend.store_translations('de', :foo => "%{scope}")
+ I18n.translate(:foo, :baz => 'baz', :locale => :de)
end
end
View
21 test/i18n_test.rb
@@ -109,18 +109,25 @@ def test_uses_passed_separator_to_normalize_keys
end
def test_can_set_exception_handler
+ previous_exception_handler = I18n.exception_handler
assert_nothing_raised { I18n.exception_handler = :custom_exception_handler }
ensure
- I18n.exception_handler = :default_exception_handler
+ I18n.exception_handler = previous_exception_handler
end
with_mocha do
- def test_uses_custom_exception_handler
+ def test_uses_custom_exception_handler_set_to_i18n_exception_handler
+ previous_exception_handler = I18n.exception_handler
I18n.exception_handler = :custom_exception_handler
I18n.expects(:custom_exception_handler)
I18n.translate :bogus
ensure
- I18n.exception_handler = :default_exception_handler # revert it
+ I18n.exception_handler = previous_exception_handler
+ end
+
+ def test_uses_custom_exception_handler_passed_as_option
+ I18n.expects(:custom_exception_handler)
+ I18n.translate(:bogus, :exception_handler => :custom_exception_handler)
end
def test_delegates_translate_to_backend
@@ -193,15 +200,17 @@ def test_localize_object_raises_argument_error
end
def test_proc_exception_handler
+ previous_exception_handler = I18n.exception_handler
I18n.exception_handler = Proc.new { |exception, locale, key, options|
"No exception here! [Proc handler]"
}
assert_equal "No exception here! [Proc handler]", I18n.translate(:test_proc_handler)
ensure
- I18n.exception_handler = :default_exception_handler
+ I18n.exception_handler = previous_exception_handler
end
def test_class_exception_handler
+ previous_exception_handler = I18n.exception_handler
I18n.exception_handler = Class.new do
def call(exception, locale, key, options)
"No exception here! [Class handler]"
@@ -209,7 +218,7 @@ def call(exception, locale, key, options)
end.new
assert_equal "No exception here! [Class handler]", I18n.translate(:test_class_handler)
ensure
- I18n.exception_handler = :default_exception_handler
+ I18n.exception_handler = previous_exception_handler
end
test "I18n.with_locale" do
@@ -231,7 +240,7 @@ def call(exception, locale, key, options)
assert_equal :pl, I18n.locale
assert_equal 'Foo in :pl', I18n.t(:foo)
end
-
+
I18n.with_locale(:en) do
assert_equal :en, I18n.locale
assert_equal 'Foo in :en', I18n.t(:foo)
Please sign in to comment.
Something went wrong with that request. Please try again.