Hello,
I have never posted here, neither contributed to any open source project. So I am not sure how to go about this. This is a proposal and a question at the same time. Trying to know if what is my head is valuable for Rails. It is just a tiny change.
In the Rails Internationalisation guide it shows how to set up a customer exception handler for I18n.
In the documentation is explained that Rails TranslationHelper will not use this custom exception handler.
Another example where the default behavior is less desirable is the Rails TranslationHelper which provides the method
#t
(as well as#translate
). When aMissingTranslationData
exception occurs in this context, the helper wraps the message into a span with the CSS classtranslation_missing
.
I think it would be useful to be able to have a I18n custom exception handler even for the Rails Views and pass it the default translations.
It would allow to raise exceptions locally while failing silently in production and at the same time returning the default translation. I show this in the following Exception handler:
module I18n
class JustRaiseExceptionHandler < ExceptionHandler
def call(exception, locale, key, options)
raise exception if Rails.env.development?
Rails.logger.error "#{exception} #{locale} #{key}"
options[:default_translation]
end
end
end
I have added two lines to the ActionView::Helpers::TranslationHelper#translate that would allow this behaviour with a given configuration. The only change is inside the rescue clause, after line 91 in original implementation.
Do you think this is valuable for Rails?
Regards,
David.
def translate(key, options = {})
options = options.dup
has_default = options.has_key?(:default)
remaining_defaults = Array(options.delete(:default)).compact
if has_default && !remaining_defaults.first.kind_of?(Symbol)
options[:default] = remaining_defaults
end
# If the user has explicitly decided to NOT raise errors, pass that option to I18n.
# Otherwise, tell I18n to raise an exception, which we rescue further in this method.
# Note: `raise_error` refers to us re-raising the error in this method. I18n is forced to raise by default.
if options[:raise] == false || (options.key?(:rescue_format) && options[:rescue_format].nil?)
raise_error = false
i18n_raise = false
else
raise_error = options[:raise] || options[:rescue_format] || ActionView::Base.raise_on_missing_translations
i18n_raise = true
end
if html_safe_translation_key?(key)
html_safe_options = options.dup
options.except(*I18n::RESERVED_KEYS).each do |name, value|
unless name == :count && value.is_a?(Numeric)
html_safe_options[name] = ERB::Util.html_escape(value.to_s)
end
end
translation = I18n.translate(scope_key_by_partial(key), html_safe_options.merge(raise: i18n_raise))
translation.respond_to?(:html_safe) ? translation.html_safe : translation
else
I18n.translate(scope_key_by_partial(key), options.merge(raise: i18n_raise))
end
rescue I18n::MissingTranslationData => e
if remaining_defaults.present?
translate remaining_defaults.shift, options.merge(default: remaining_defaults)
else
raise e if raise_error
keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope])
default_translation = content_tag('span', keys.last.to_s.titleize, :class => 'translation_missing', :title => "translation missing: #{keys.join('.')}")
if custom_exception_handler?
I18n.exception_handler.call(e, e.locale, e.key, {default_translation: default_translation})
else
default_translation
end
end
end
alias :t :translate
private
def custom_exception_handler?
true # ActionView::Base.use_i18n_exception_handler
end