Search code examples
ruby-on-railsrubyrails-i18ni18n-gem

rails I18n wrong parameter order in route (id as locale and id is nil)


I want to internationalize an external code (see github) with i18n for rails. I have read the guide for Rails Internationalization (I18n) API. Translating the text is not a problem, but the underlying code seems not to work properly anymore in all situations. Unfortunately, I'm not a rails/ruby expert.

From the log:

FATAL -- : ActionView::Template::Error (No route matches {:action=>"edit", :controller=>"plans", :format=>nil, :id=>nil, :locale=>115} missing required keys: [:id]):

So the problem is, that the parameter for the id (=115) is passed as locale instead as id.

To get i18n working I added the following code into app/controllers/application_controller.rb:

...
  before_action :set_locale

  protected

  def set_locale
    I18n.locale = params[:locale] || I18n.default_locale
  end

  def default_url_options(options = {})
    { locale: I18n.locale }.merge options
  end
...

Moreover, I wrapped the original routes in config/routes.rb:

Dmptool2::Application.routes.draw do
  scope "(:locale)", locale: /en|de/ do
    a lot of original routes
  end
end

Therefore, the question is, is there a missing route or is there a problem inside the code or is it just my fault. Besides translating text and buttons, I haven't changed the original code. The original routes.rb can be found on github (sry, I can't post the link because I don't have enough reputation). Any suggestions / help would be perfect.

Edit I think I'm a little bit closer. Maybe now it's more clear, why it isn't working. First of all the "full" stacktrace:

F, [2015-05-04T16:43:58.600384 #19289] FATAL -- : 
ActionView::Template::Error (No route matches {:action=>"edit", :controller=>"plans", :format=>nil, :id=>nil, :locale=>#<Plan id: 158, name: "asdfe", requirements_template_id: 59, solicitation_identifier: "",
 submission_deadline: nil, visibility: :public, created_at: "2015-05-04 14:41:33", updated_at: "2015-05-04 14:43:48", current_plan_state_id: 300>} missing required keys: [:id]):
    2: 
    3: The plan "<%= @vars[:plan].name %>" has been completed.
    4: 
    5: If you have questions pertaining to this action, please visit the DMP Overview page at <%= edit_plan_url(@vars[:plan]) %>
    6: 
    7: <%= render :partial => 'users_mailer/notification_boilerplate.text.erb' %>
  app/views/users_mailer/dmp_owners_and_co_committed.text.erb:5:in `_app_views_users_mailer_dmp_owners_and_co_committed_text_erb__754483862330985648_69917281861000'
  app/mailers/users_mailer.rb:32:in `notification'
  app/models/concerns/plan_email.rb:50:in `block in email_dmp_saved'
  app/models/concerns/plan_email.rb:49:in `email_dmp_saved'
  app/models/plan_state.rb:31:in `update_current_plan_state'
  app/controllers/plan_states_controller.rb:97:in `create_plan_state'
  app/controllers/plan_states_controller.rb:73:in `committed'

If I hit the "done" Button on the webpage, the function committed is called, wich calls create_plan_state(:committed). Within the definition of create_plan_state, there is the statement plan_state = PlanState.create( plan_id: @plan.id, state: state, user_id: current_user.id). This triggers a callback for after_create: update_current_plan_state:

def update_current_plan_state
  #update the current plan pointer in the plan model
  p = self.plan
  p.current_plan_state_id = self.id
  p.save!
end

Now, this triggers after_save: email_dmp_saved:

def email_dmp_saved
...
if current_state.state == :committed
  users = self.users
  users.delete_if {|u| !u[:prefs][:dmp_owners_and_co][:committed]}
  users.each do |user|
    UsersMailer.notification(
        user.email,
        "PLAN COMPLETED: #{self.name}",
        "dmp_owners_and_co_committed",
        {:user => user, :plan => self } ).deliver
  end

I think the definition of the notification is not important. But the 3rd last line calls "dmp_owners_and_co_committed", which is defined as:

Hello <%= @vars[:user].full_name %>,

The plan "<%= @vars[:plan].name %>" has been completed.

If you have questions pertaining to this action, please visit the DMP Overview page at <%= edit_plan_url(@vars[:plan]) %>

<%= render :partial => 'users_mailer/notification_boilerplate.text.erb' %>

And in _notification_boilerplate.text.erb there is:

You may change your notification preferences at <%= edit_user_url(@vars[:user].id) %>#tab_tab2 .

I think the problem is edit_plan_url and edit_user_url. Because if I add some random text as parameter it works...:

edit_plan_url("de",@vars[:plan])
and in _notification:
edit_user_url("de",@vars[:user].id)

The question is, why is it working? Is there a way to print the created route? Because in the stacktrace the route doesn't match because format and id is nil. Now I want to see the new route in order to know where my random string "de" is placed.


Solution

  • Looks like your routes are expecting two params, and ordering it as it comes. There's a way to avoid it by using a named hash into the params passed:

    edit_plan_url(id: @vars[:plan].id)
    

    The Rails Automagic will use the symbol to identify the param and avoid the problem.