From 9d85541a5ed34a2afca8e49715945ed0b94a2935 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sun, 27 Feb 2011 14:19:22 +0100 Subject: [PATCH] Switch from I18n::MissingTranslationData (which is an Exception) to I18n::MissingTranslation (which is a plain Object) for the default case. Create an I18n::MissingTranslationData exception only if it needs to be raised to the user. --- lib/i18n.rb | 9 +++--- lib/i18n/backend/base.rb | 4 +-- lib/i18n/backend/cache.rb | 2 +- lib/i18n/backend/chain.rb | 4 +-- lib/i18n/backend/fallbacks.rb | 5 ++- lib/i18n/exceptions.rb | 46 +++++++++++++++++++--------- lib/i18n/rails/translation_helper.rb | 4 +-- test/backend/exceptions_test.rb | 7 +++++ test/i18n_test.rb | 8 ++--- 9 files changed, 55 insertions(+), 34 deletions(-) diff --git a/lib/i18n.rb b/lib/i18n.rb index d8cd5cfb..b49479c3 100755 --- a/lib/i18n.rb +++ b/lib/i18n.rb @@ -156,7 +156,7 @@ def translate(*args) backend.translate(locale, key, options) end end - result.is_a?(MissingTranslationData) ? handle_exception(handling, result, locale, key, options) : result + result.is_a?(MissingTranslation) ? handle_exception(handling, result, locale, key, options) : result end alias :t :translate @@ -266,7 +266,7 @@ def normalize_keys(locale, key, scope, separator = nil) # Any exceptions thrown in translate will be sent to the @@exception_handler # which can be a Symbol, a Proc or any other Object unless they're forced to - # be raised or thrown (MissingTranslationData). + # be raised or thrown (MissingTranslation). # # If exception_handler is a Symbol then it will simply be sent to I18n as # a method call. A Proc will simply be called. In any other case the @@ -285,7 +285,7 @@ def normalize_keys(locale, key, scope, separator = nil) def handle_exception(handling, exception, locale, key, options) case handling when :raise - raise exception + raise(exception.respond_to?(:to_exception) ? exception.to_exception : exception) when :throw throw :exception, exception else @@ -325,8 +325,7 @@ def normalize_translation_keys(locale, key, scope, separator = nil) 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 + exception.is_a?(MissingTranslation) ? exception.message : raise(exception) end end end diff --git a/lib/i18n/backend/base.rb b/lib/i18n/backend/base.rb index 899506b3..a9e70fdf 100644 --- a/lib/i18n/backend/base.rb +++ b/lib/i18n/backend/base.rb @@ -34,7 +34,7 @@ def translate(locale, key, options = {}) default(locale, key, default, options) : resolve(locale, key, entry, options) end - throw(:exception, I18n::MissingTranslationData.new(locale, key, options)) if entry.nil? + throw(:exception, I18n::MissingTranslation.new(locale, key, options)) if entry.nil? entry = entry.dup if entry.is_a?(String) entry = pluralize(locale, entry, count) if count @@ -119,7 +119,7 @@ def resolve(locale, object, subject, options = {}) subject end end - result unless result.is_a?(MissingTranslationData) + result unless result.is_a?(MissingTranslation) end # Picks a translation from an array according to English pluralization diff --git a/lib/i18n/backend/cache.rb b/lib/i18n/backend/cache.rb index 0f19744d..67573418 100644 --- a/lib/i18n/backend/cache.rb +++ b/lib/i18n/backend/cache.rb @@ -70,7 +70,7 @@ def translate(locale, key, options = {}) def fetch(cache_key, &block) result = _fetch(cache_key, &block) - throw(:exception, result) if result.is_a?(MissingTranslationData) + throw(:exception, result) if result.is_a?(MissingTranslation) result = result.dup if result.frozen? rescue result result end diff --git a/lib/i18n/backend/chain.rb b/lib/i18n/backend/chain.rb index 2a483f1d..5a0c59b5 100644 --- a/lib/i18n/backend/chain.rb +++ b/lib/i18n/backend/chain.rb @@ -54,7 +54,7 @@ def translate(locale, key, default_options = {}) end return namespace if namespace - throw(:exception, I18n::MissingTranslationData.new(locale, key, options)) + throw(:exception, I18n::MissingTranslation.new(locale, key, options)) end def localize(locale, object, format = :default, options = {}) @@ -63,7 +63,7 @@ def localize(locale, object, format = :default, options = {}) result = backend.localize(locale, object, format, options) and return result end end - throw(:exception, I18n::MissingTranslationData.new(locale, format, options)) + throw(:exception, I18n::MissingTranslation.new(locale, format, options)) end protected diff --git a/lib/i18n/backend/fallbacks.rb b/lib/i18n/backend/fallbacks.rb index 85c57e35..70c2c04b 100644 --- a/lib/i18n/backend/fallbacks.rb +++ b/lib/i18n/backend/fallbacks.rb @@ -29,8 +29,7 @@ module Fallbacks # locale :"de-DE" it might try the locales :"de-DE", :de and :en # (depends on the fallbacks implementation) until it finds a result with # the given options. If it does not find any result for any of the - # locales it will then raise a MissingTranslationData exception as - # usual. + # locales it will then throw MissingTranslation as usual. # # The default option takes precedence over fallback locales # only when it's a Symbol. When the default contains a String or a Proc @@ -49,7 +48,7 @@ def translate(locale, key, options = {}) options.delete(:fallback) return super(locale, nil, options.merge(:default => default)) if default - throw(:exception, I18n::MissingTranslationData.new(locale, key, options)) + throw(:exception, I18n::MissingTranslation.new(locale, key, options)) end def extract_string_or_lambda_default!(options) diff --git a/lib/i18n/exceptions.rb b/lib/i18n/exceptions.rb index 6b02b760..45aba2f5 100644 --- a/lib/i18n/exceptions.rb +++ b/lib/i18n/exceptions.rb @@ -6,7 +6,7 @@ module I18n class ExceptionHandler include Module.new { def call(exception, locale, key, options) - if exception.is_a?(MissingTranslationData) + if exception.is_a?(MissingTranslation) options[:rescue_format] == :html ? exception.html_message : exception.message else raise exception @@ -33,25 +33,41 @@ def initialize(filename) end end - class MissingTranslationData < ArgumentError - attr_reader :locale, :key, :options + class MissingTranslation + module Base + 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 initialize(locale, key, options = nil) + @key, @locale, @options = key, locale, options.dup || {} + options.each { |k, v| self.options[k] = v.inspect if v.is_a?(Proc) } + end - def html_message - key = keys.last.to_s.gsub('_', ' ').gsub(/\b('?[a-z])/) { $1.capitalize } - %(#{key}) - end + def html_message + key = keys.last.to_s.gsub('_', ' ').gsub(/\b('?[a-z])/) { $1.capitalize } + %(#{key}) + end + + def keys + @keys ||= I18n.normalize_keys(locale, key, options[:scope]).tap do |keys| + keys << 'no key' if keys.size < 2 + end + end + + def message + "translation missing: #{keys.join('.')}" + end + alias :to_s :message - def keys - @keys ||= I18n.normalize_keys(locale, key, options[:scope]).tap do |keys| - keys << 'no key' if keys.size < 2 + def to_exception + MissingTranslationData.new(locale, key, options) end end + + include Base + end + + class MissingTranslationData < ArgumentError + include MissingTranslation::Base end class InvalidPluralizationData < ArgumentError diff --git a/lib/i18n/rails/translation_helper.rb b/lib/i18n/rails/translation_helper.rb index b9bf5f14..9da0cbee 100644 --- a/lib/i18n/rails/translation_helper.rb +++ b/lib/i18n/rails/translation_helper.rb @@ -9,7 +9,7 @@ class ExceptionHandler # are forced to html_safe include Module.new { def call(exception, locale, key, options) - exception.is_a?(MissingTranslationData) ? super.html_safe : super + exception.is_a?(MissingTranslation) ? super.html_safe : super end } end @@ -22,7 +22,7 @@ module TranslationHelper # Delegates to I18n#translate but also performs three additional functions. # # First, it'll pass the :rescue_format => :html option to I18n so that any caught - # MissingTranslationData exceptions will be turned into inline spans that + # MissingTranslation exceptions will be turned into inline spans that # # * have a "translation-missing" class set, # * contain the missing key as a title attribute and diff --git a/test/backend/exceptions_test.rb b/test/backend/exceptions_test.rb index fb2bb483..48fbc430 100644 --- a/test/backend/exceptions_test.rb +++ b/test/backend/exceptions_test.rb @@ -5,6 +5,13 @@ def setup I18n.backend = I18n::Backend::Simple.new end + test "throw message: MissingTranslation message from #translate includes the given scope and full key" do + exception = catch(:exception) do + I18n.t(:'baz.missing', :scope => :'foo.bar', :throw => true) + end + assert_equal "translation missing: en.foo.bar.baz.missing", exception.message + end + test "exceptions: MissingTranslationData message from #translate includes the given scope and full key" do begin I18n.t(:'baz.missing', :scope => :'foo.bar', :raise => true) diff --git a/test/i18n_test.rb b/test/i18n_test.rb index d8acca66..ad7872e2 100644 --- a/test/i18n_test.rb +++ b/test/i18n_test.rb @@ -197,8 +197,8 @@ def setup test "can use a lambda as an exception handler" do begin previous_exception_handler = I18n.exception_handler - I18n.exception_handler = Proc.new { |exception, locale, key, options| exception } - assert_equal I18n::MissingTranslationData, I18n.translate(:test_proc_handler).class + I18n.exception_handler = Proc.new { |exception, locale, key, options| key } + assert_equal :test_proc_handler, I18n.translate(:test_proc_handler) ensure I18n.exception_handler = previous_exception_handler end @@ -208,9 +208,9 @@ def setup begin previous_exception_handler = I18n.exception_handler I18n.exception_handler = Class.new do - def call(exception, locale, key, options); exception; end + def call(exception, locale, key, options); key; end end.new - assert_equal I18n::MissingTranslationData, I18n.translate(:test_proc_handler).class + assert_equal :test_proc_handler, I18n.translate(:test_proc_handler) ensure I18n.exception_handler = previous_exception_handler end