Blob Blame History Raw
# backported from 3.0.10 to 3.0.5
diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb
index 3d3df01..00963cd 100644
--- a/actionpack/lib/action_view/helpers/translation_helper.rb
+++ b/actionpack/lib/action_view/helpers/translation_helper.rb
@@ -24,11 +24,18 @@ module ActionView
       # naming convention helps to identify translations that include HTML tags so that
       # you know what kind of output to expect when you call translate in a template.
       def translate(key, options = {})
-        translation = I18n.translate(scope_key_by_partial(key), options.merge!(:raise => true))
-        if html_safe_translation_key?(key) && translation.respond_to?(:html_safe)
-          translation.html_safe
+        options.merge!(:raise => true)
+        options.merge!(:rescue_format => :html) unless options.key?(:rescue_format)
+        if html_safe_translation_key?(key)
+          html_safe_options = options.dup
+          options.except(*I18n::RESERVED_KEYS).each do |name, value|
+            html_safe_options[name] = ERB::Util.html_escape(value.to_s)
+          end
+          translation = I18n.translate(scope_key_by_partial(key), html_safe_options)
+
+          translation.respond_to?(:html_safe) ? translation.html_safe : translation
         else
-          translation
+          I18n.translate(scope_key_by_partial(key), options)
         end
       rescue I18n::MissingTranslationData => e
         keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope])

diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb
index cd9f54e..cabb29c 100644
--- a/actionpack/test/template/translation_helper_test.rb
+++ b/actionpack/test/template/translation_helper_test.rb
@@ -6,10 +6,15 @@
 
   attr_reader :request
   def setup
+    I18n.backend.store_translations(:en,
+      :translations => {
+        :interpolated_html => '<a>Hello %{word}</a>',
+      }
+    )
   end
 
   def test_delegates_to_i18n_setting_the_raise_option
-    I18n.expects(:translate).with(:foo, :locale => 'en', :raise => true).returns("")
+    I18n.expects(:translate).with(:foo, :locale => 'en', :raise => true, :rescue_format => :html).returns("")
     translate :foo, :locale => 'en'
   end
 
@@ -19,7 +24,7 @@
   end
 
   def test_translation_returning_an_array
-    I18n.expects(:translate).with(:foo, :raise => true).returns(["foo", "bar"])
+    I18n.expects(:translate).with(:foo, :raise => true, :rescue_format => :html).returns(["foo", "bar"])
     assert_equal ["foo", "bar"], translate(:foo)
   end
 
@@ -30,34 +35,39 @@
   end
 
   def test_scoping_by_partial
-    I18n.expects(:translate).with("test.translation.helper", :raise => true).returns("helper")
+    I18n.expects(:translate).with("test.translation.helper", :raise => true, :rescue_format => :html).returns("helper")
     @view = ActionView::Base.new(ActionController::Base.view_paths, {})
     assert_equal "helper", @view.render(:file => "test/translation")
   end
 
   def test_scoping_by_partial_of_an_array
-    I18n.expects(:translate).with("test.scoped_translation.foo.bar", :raise => true).returns(["foo", "bar"])
+    I18n.expects(:translate).with("test.scoped_translation.foo.bar", :raise => true, :rescue_format => :html).returns(["foo", "bar"])
     @view = ActionView::Base.new(ActionController::Base.view_paths, {})
     assert_equal "foobar", @view.render(:file => "test/scoped_translation")
   end
 
   def test_translate_does_not_mark_plain_text_as_safe_html
-    I18n.expects(:translate).with("hello", :raise => true).returns("Hello World")
+    I18n.expects(:translate).with("hello", :raise => true, :rescue_format => :html).returns("Hello World")
     assert_equal false, translate("hello").html_safe?
   end
 
   def test_translate_marks_translations_named_html_as_safe_html
-    I18n.expects(:translate).with("html", :raise => true).returns("<a>Hello World</a>")
+    I18n.expects(:translate).with("html", :raise => true, :rescue_format => :html).returns("<a>Hello World</a>")
     assert translate("html").html_safe?
   end
 
   def test_translate_marks_translations_with_a_html_suffix_as_safe_html
-    I18n.expects(:translate).with("hello_html", :raise => true).returns("<a>Hello World</a>")
+    I18n.expects(:translate).with("hello_html", :raise => true, :rescue_format => :html).returns("<a>Hello World</a>")
     assert translate("hello_html").html_safe?
   end
 
+  def test_translate_escapes_interpolations_in_translations_with_a_html_suffix
+    assert_equal '<a>Hello &lt;World&gt;</a>', translate(:'translations.interpolated_html', :word => '<World>', :raise => true, :rescue_format => :html)
+    assert_equal '<a>Hello &lt;World&gt;</a>', translate(:'translations.interpolated_html', :word => stub(:to_s => "<World>"))
+  end
+
   def test_translation_returning_an_array_ignores_html_suffix
-    I18n.expects(:translate).with(:foo_html, :raise => true).returns(["foo", "bar"])
+    I18n.expects(:translate).with(:foo_html, :raise => true, :rescue_format => :html).returns(["foo", "bar"])
     assert_equal ["foo", "bar"], translate(:foo_html)
   end
 end