Wrap all handlers for `rescue_from` inside block

I am trying to output localized messages to the user inside all rescue_from blocks, but can’t get it to work. I want to avoid the boilerplate of wrapping each handler inside a I18n.with_locale block manually. My general approach is this:

class ApplicationController < ActionController::Base
  def self.rescue_from_with_locale(*, **, &)
    self.rescue_from(*, **) { I18n.with_locale(params[:locale], &) }
  end

  rescue_from_with_locale Error1 do
    render(plain: t("error1_happened"))
  end
  rescue_from_with_locale Error2 do
    render(plain: t("error2_happened"))
  end
end

However, when such an error triggers, Rails raises NoMethodError (undefined method 't' for class ApplicationController)

So obviously this happens because the original handler is executed in the class context but is supposed to be executed in the instance context instead. The question is how can I ensure the entire handler is executed in the instance context? I found no way of doing so.

Thanks for any tips!

I’d keep I18n.with_locale, but call the original handler with instance_exec inside it. Yielding a previously captured block does not change that block’s self; Rails changes the context of your outer wrapper, not of the original handler passed through &.

For the block-only helper in your example:

def self.rescue_from_with_locale(*exceptions, &handler)
  rescue_from(*exceptions) do |exception|
    locale = params[:locale] || I18n.default_locale
    I18n.with_locale(locale) do
      if handler.arity == 0
        instance_exec(&handler)
      else
        instance_exec(exception, &handler)
      end
    end
  end
end

Here the outer block already runs on the controller instance, so instance_exec also makes that instance the receiver of t and render in the original block. The arity check preserves both zero-argument handlers and handlers receiving the exception. This example intentionally handles block registrations, rather than the with: form.

Also validate the requested locale against the locales your application supports, so an invalid parameter does not raise another exception inside the error handler.

References: Rails rescue handlers and locale handling.

Ah, I was missing the instance_exec, it is working now as intended, thank you very much!