Skip to content

Commit

Permalink
Refactor Action View translate helper
Browse files Browse the repository at this point in the history
This refactor incidentally fixes a corner case when `translate` is
called with a block, the translation is missing, and
`debug_missing_translation` is false.

This commit adds a test for the above corner case, and additional tests
for existing behavior.
  • Loading branch information
jonathanhefner committed Sep 11, 2020
1 parent 99a0254 commit 98a76a5
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 23 deletions.
63 changes: 40 additions & 23 deletions actionview/lib/action_view/helpers/translation_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,24 +88,15 @@ def translate(key, **options)
fully_resolved_key = scope_key_by_partial(key)

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

html_safe_options = html_escape_translation_options(options)
html_safe_options[:default] = MISSING_TRANSLATION unless html_safe_options[:default].blank?

translation = I18n.translate(fully_resolved_key, **html_safe_options.merge(raise: i18n_raise))

if translation.equal?(MISSING_TRANSLATION)
translated_text = options[:default].first
elsif translation.respond_to?(:map)
translated_text = translation.map { |element| element.respond_to?(:html_safe) ? element.html_safe : element }
else
translated_text = translation.respond_to?(:html_safe) ? translation.html_safe : translation
translated_text = html_safe_translation(translation)
end
else
translated_text = I18n.translate(fully_resolved_key, **options.merge(raise: i18n_raise))
Expand All @@ -122,18 +113,7 @@ def translate(key, **options)
else
raise e if raise_error

keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope])
title = +"translation missing: #{keys.join('.')}"

interpolations = options.except(:default, :scope)

if interpolations.any?
title << ", " << interpolations.map { |k, v| "#{k}: #{ERB::Util.html_escape(v)}" }.join(", ")
end

return title unless ActionView::Base.debug_missing_translation

translated_fallback = content_tag("span", keys.last.to_s.titleize, class: "translation_missing", title: title)
translated_fallback = missing_translation(e, options)

if block_given?
yield(translated_fallback, scope_key_by_partial(key))
Expand Down Expand Up @@ -172,9 +152,46 @@ def scope_key_by_partial(key)
end
end

def html_escape_translation_options(options)
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

html_safe_options
end

def html_safe_translation_key?(key)
/(?:_|\b)html\z/.match?(key.to_s)
end

def html_safe_translation(translation)
if translation.respond_to?(:map)
translation.map { |element| element.respond_to?(:html_safe) ? element.html_safe : element }
else
translation.respond_to?(:html_safe) ? translation.html_safe : translation
end
end

def missing_translation(error, options)
keys = I18n.normalize_keys(error.locale, error.key, error.options[:scope])

title = +"translation missing: #{keys.join(".")}"

interpolations = options.except(:default, :scope)
if interpolations.any?
title << ", " << interpolations.map { |k, v| "#{k}: #{ERB::Util.html_escape(v)}" }.join(", ")
end

if ActionView::Base.debug_missing_translation
content_tag("span", keys.last.to_s.titleize, class: "translation_missing", title: title)
else
title
end
end
end
end
end
30 changes: 30 additions & 0 deletions actionview/test/template/translation_helper_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,15 @@ def test_raises_missing_translation_message_with_raise_config_option
ActionView::Base.raise_on_missing_translations = false
end

def test_raise_arg_overrides_raise_config_option
ActionView::Base.raise_on_missing_translations = true

expected = "translation missing: en.translations.missing"
assert_equal expected, translate(:"translations.missing", raise: false)
ensure
ActionView::Base.raise_on_missing_translations = false
end

def test_raises_missing_translation_message_with_raise_option
assert_raise(I18n::MissingTranslationData) do
translate(:"translations.missing", raise: true)
Expand Down Expand Up @@ -164,6 +173,16 @@ def test_missing_translation_scoped_by_partial_yield_block
assert_equal expected, view.render(template: "translations/templates/missing_yield_block").strip
end

def test_missing_translation_scoped_by_partial_yield_block_without_debug_wrapper
old_debug_missing_translation = ActionView::Base.debug_missing_translation
ActionView::Base.debug_missing_translation = false

expected = "translations.templates.missing_yield_block.missing: translation missing: en.translations.templates.missing_yield_block.missing"
assert_equal expected, view.render(template: "translations/templates/missing_yield_block").strip
ensure
ActionView::Base.debug_missing_translation = old_debug_missing_translation
end

def test_missing_translation_with_default_scoped_by_partial_yield_block
expected = "translations.templates.missing_with_default_yield_block.missing: Default"
assert_equal expected, view.render(template: "translations/templates/missing_with_default_yield_block").strip
Expand Down Expand Up @@ -229,6 +248,12 @@ def test_translate_with_missing_default_and_raise_option
end
end

def test_translate_with_html_key_and_missing_default_and_raise_option
assert_raise(I18n::MissingTranslationData) do
translate(:"translations.missing_html", default: :"translations.missing_html", raise: true)
end
end

def test_translate_with_two_defaults_named_html
translation = translate(:'translations.missing', default: [:'translations.missing_html', :'translations.hello_html'])
assert_equal "<a>Hello World</a>", translation
Expand Down Expand Up @@ -289,6 +314,11 @@ def test_translate_with_nil_default
assert_nil translation
end

def test_translate_bulk_lookup
translations = translate([:"translations.foo", :"translations.foo"])
assert_equal ["Foo", "Foo"], translations
end

def test_translate_does_not_change_options
options = {}
if RUBY_VERSION >= "2.7"
Expand Down

0 comments on commit 98a76a5

Please sign in to comment.