Skip to content

Wrong I18n Scope used for slots in nested components in Ruby on Rails #2513

@henrikbjorn

Description

@henrikbjorn

In our application after upgrading to the new major version 4 we are seeing issues with how i18n scopes in our rails application is resolved when used with components.

We have a table component with the following api that uses 3 levels of nesting in the component.

<# app/views/test/index.html.erb %>
  <%= render(TableComponent.new) do |table| %>
    <% table.with_row do |row| %>
      <% row.with_cell do %>
        <%= t('.hello') %>
      <% end %>
    <% end %>
  <% end %>

Previously this would be resolved to test.index.hello i18n key. After our upgrade it will try table_component.hello instead.

Recently there was merged something that looked like a fix for this on the main branch. But it dosen't resolve the issue.

Curiously it works if the nested is only 2 levels and not 3 as in my example (at least on the main branch)

Steps to reproduce

Here is a single file test case that shows the bug https://gist.github.com/henrikbjorn/71bfad72e8810d4ac3eae2223c67d493

Running it will fail with

Fetching https://github.com/viewcomponent/view_component.git
Fetching gem metadata from https://rubygems.org/...........
Resolving dependencies...
Run options: --seed 17537

# Running:

F

Finished in 0.012763s, 78.3515 runs/s, 156.7030 assertions/s.

  1) Failure:
SlotI18nTest#test_t('.key')_in_nested_slot_should_use_view's_i18n_scope [test.rb:77]:
Expected 'test.index.hello' but got: <table><tr><td>      Hello from table
</td></tr></table>.
Expected # encoding: ASCII-8BIT
#    valid: true
"<table><tr><td>      Hello from table\n</td></tr></table>" to include "Hello from view".

1 runs, 2 assertions, 1 failures, 0 errors, 0 skips

I have created a fix locally for our application, which added to the test case https://gist.github.com/henrikbjorn/689b8295ed5dd29797a876d5ac06db7f will make the test pass

Fetching https://github.com/viewcomponent/view_component.git
Fetching gem metadata from https://rubygems.org/...........
Resolving dependencies...
Run options: --seed 46458

# Running:

.

Finished in 0.012941s, 77.2738 runs/s, 154.5476 assertions/s.

1 runs, 2 assertions, 0 failures, 0 errors, 0 skips

The fix as a module, not sure if it is good enough for a PR, since I am not really familiar with view component internals

module VirtualPathFix
  def render_in(view_context, &block)
    current_virtual_path = view_context.instance_variable_get(:@virtual_path)
    stored_original = view_context.instance_variable_get(:@__vc_original_view_virtual_path)
    is_view_path = current_virtual_path && !current_virtual_path.to_s.include?("component")

    @__vc_original_view_virtual_path = if is_view_path && current_virtual_path != stored_original
      current_virtual_path
    else
      stored_original
    end

    view_context.instance_variable_set(:@__vc_original_view_virtual_path, @__vc_original_view_virtual_path)
    super
  end

  def with_original_virtual_path
    view_context.instance_variable_set(:@virtual_path, @__vc_original_view_virtual_path)
    yield
  ensure
    view_context.instance_variable_set(:@virtual_path, virtual_path)
  end
end

Expected behavior

I would expect the i18n key to be test.index.hello

Actual behavior

The key is is resolved to table_component.hello

Backtrace:

System configuration

Rails version: Latest 8.1 release

Ruby version: 3.4.7

Gem version: tested with latest main and latest 4.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions