Skip to content
New issue

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

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem with translations for components, defined in a gem (engine) and used in Rails application #2198

Open
gsmetal opened this issue Jan 21, 2025 · 3 comments

Comments

@gsmetal
Copy link

gsmetal commented Jan 21, 2025

I am building UI kit (as Rails engine) with a bunch of implemented basic components. And then I use this gem (pushed to private rubygems) in the application. In application, there are app-specific components, so I configured view_component_path to Rails.root.join('app/view_components').

In UI-kit components, I use sidecar translations for some built-in strings. And when I install gem from rubygems, it's component's i18n is loaded incorrectly. This code does not remove absolute path part, and it fills configuration attributes like this:

Component.identifier # => "/home/user/.asdf/installs/ruby/3.3.5/lib/ruby/gems/3.3.0/gems/mygem-0.5.1/app/view_components/mygem/component.rb"
Component.virtual_path # => "/home/user/.asdf/installs/ruby/3.3.5/lib/ruby/gems/3.3.0/gems/mygem-0.5.1/app/view_components/mygem/component"
Component.i18n_scope # => "home.user..asdf.installs.ruby.3.3.5.lib.ruby.gems.3.3.0.gems.mygem-0.5.1.app.view_components.mygem.component"

Last one is the main problem, this warning renders instead of translation:

translation missing: ru.home.user.asdf.installs.ruby.3.3.5.lib.ruby.gems.3.3.0.gems.mygem-0.5.1.app.view_components.mygem.component.placeholder, locale: ru

It seems to me, that current architecture with one general view_component_path does not allow building UI-kits flawlessly (where components could be defined in gems AND in application). As a workaround now I use this:

      def inherited(child)
        super

        # HACK: removes the first part of the gem's path. It is needed when component tranlslations comes from gem and
        # not from Rails application
        child.virtual_path.gsub!(
          /(.*#{Regexp.quote(Mygem::Engine.root.join('app/view_components').to_s)})|(\.rb)/, ''
        )
      end
@reeganviljoen
Copy link
Collaborator

@gsmetal thank you for submitting this bug, and using view_component!
someone, either me or someone else, will get back to you about this, either with failing specs or a possible fix if there is one

@boardfish
Copy link
Collaborator

Hi @gsmetal! The workaround you've added there suggests that this is a gem-specific concern – #2124 is one of the first steps towards reworking config to better support this, but I think this provides a really good practical example of how configuration will explicitly accommodate engines.

My vision here is that engines will define their own base component class inheriting from ViewComponent::Base, and all components inheriting from that will obey its config, e.g.:

(This is pseudocode, interface not final.)

module Mygem
  class ApplicationComponent < ViewComponent::Base
    view_component_config do |config|
      # Perhaps this can even be done implicitly/magically somehow?
      config.root_path Mygem::Engine.root
    end
  end
end
module Mygem
  # now that we're inheriting from the above, this will respect the root path.
  class SomeComponent < ApplicationComponent
  end
end

In the interim, the workaround you've got there is great, though – thank you for sharing it!

@gsmetal
Copy link
Author

gsmetal commented Jan 22, 2025

Hi @boardfish! Good to know, that you are looking in this direction 👍

I haven't gotten into that discussion yet, but from my point of view in this current situation, the common approach with an array of paths (like config.autoload_paths, config.i18n.load_path, config.lookbook.page_paths and even config.preview_paths in view_component) would fit: we would have several paths where we keep components. And then (maybe temporary until more general solution) we could use it in virtual_path computing:

virtual_path = child.identifier.dup
ViewComponent::Base.config.view_component_paths.each do |components_path|
  virtual_path.gsub!(/(.*#{Regexp.quote(components_path)})|(\.rb)/, '')
end
child.virtual_path = virtual_path

And magically gem's components' path could be configured in engine's initializer/initialization like this:

ActiveSupport.on_load(:view_component) do
  config.view_component_paths << Mygem::Engine.root.join('app/view_components').to_s
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants