Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

Wrong I18n scope when translate is called from a block #10444

Closed
alexisbernard opened this Issue May 3, 2013 · 6 comments

Comments

Projects
None yet
4 participants

I think that translate picks the wrong I18n scope in a particualr case when called inside a block:

When a template defines a block, that is passed to a partial, which calls the given block, then translate returns the translation scoped to the partial. I think the transation should be scoped from where the block has been defined instead.

Here is an example to clarify my explanation:

app/views/users/show.html.erb
<%= users_menu do %>
  <%= t ".foo" %>
  <%# Lookup for key "users.menu.foo" instead of "users.show.foo" %>
<% end %>
app/helpers/users_helpers.rb
module UsersHelper
  def users_menu(&block)
    render("menu", block: block)
  end
end
app/views/users/_menu.html.erb
<% block.call %>

To my opinion translate should scope form where the block is defined instead of the where the block is called, because it's more natural and also to behave like content_for.

A solution to fix that should be working around scope_key_by_partial and the @virtual_path variable.

Does anyone share the same opinion before I try to fix that?

I'm no Rails framework contributor, but I just stumbled about this very issue today. I was surprised to discover that translation calls were evaluated in the context of the partial, rather than the block passed to it. It makes no sense to evaluate block translation calls in the context of the partial as it would mean polluting the partials namespace in I18n, unless using fully qualified keys. I would appreciate a fix.

I notice the same error, anyone can help us ?

@alexisbernard alexisbernard added the stale label Apr 23, 2014

Owner

rafaelfranca commented May 1, 2014

This issue has been automatically marked as stale because it has not been commented on for at least
three months.

The resources of the Rails team are limited, and so we are asking for your help.

If you can still reproduce this error on the 4-1-stable, 4-0-stable branches or on master,
please reply with all of the information you have about it in order to keep the issue open.

Thank you for all your contributions.

This issue is still present in Rails 4.1

I guess that when the block is called it presumeably uses the @virtual_path variable when performing t calls. However, the block is not called until its rendered, and at this point the @virtual_path variable has changed to the target partial rather than the defining partial.

Owner

rafaelfranca commented May 2, 2014

I think this behavior is correct and it is how ruby works. The block run in the context of the menu view not in the context of the show view. This is why it get the menu scope.

I have examined the issue now and I think I agree. I think the cause of the confusion can be illustrated here:

class Foo
  @virtual_path = "foo"
  class << self

    def execute_block(&block)
      block.call
    end

    def t(key)
      "#{@virtual_path}#{key}"
    end

  end
end

class Bar
  @virtual_path = "bar"
  class << self

    def call_foo
      Foo.execute_block do
        t('.menu')
      end
    end

    def t(key)
      "#{@virtual_path}#{key}"
    end

  end
end

puts Bar.call_foo
=> "bar.menu"

And thus, if you think of your views as seperate objects, it's very peculiar that @alexisbernards example behaves differently.

However, in Rails all views are, AFAIK, rendered in the same object.

So to fit the above example into Rails, it would look like this:

class Rails
  @virtual_path = nil
  class << self

    def render_source
      @virtual_path = "source"
      execute_block do
        t('.menu')
      end
    end

    def execute_block(&block)
      @virtual_path = "destination"
      block.call
    end

    def t(key)
      "#{@virtual_path}#{key}"
    end

  end
end

puts Rails.render_source
=> "destination.menu"

The mistake here is postponing the evaluation of the block until rendering the destination partial.

If you want to use the calling partial as the scope, you can evaluate the block immediately. So to rewrite @alexisbernards helper, it would look like this:

module UsersHelper
  def users_menu(&block)
    block_value_evaluated_while_virtual_path_is_index = block.call
    render("menu", some_html_string: block_value_evaluated_while_virtual_path_is_index)
  end
end

And inside the show method, it would just read as

<%= some_html_string %>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment