Sidekiq: I18n not working with delay and email

Created on 2 Mar 2013  Â·  10Comments  Â·  Source: mperham/sidekiq

When using Mailer.delay.password_reset(self) with an i18n email, the language is always set to english. if I remove the .delay method and put back .deliver, the mail is delivered with the correct language.

mailers/mailer.rb

class Mailer < ActionMailer::Base
  default :from       => '"Lodgem" <[email protected]>',
          :'reply-to' => '"Lodgem" <[email protected]>'

  def password_reset(user)
    @user = user

    params = {
      to: user.email,
      subject: t('email.password_reset.subject')
    }

    mail(params)
  end
end

models/user.rb

def send_password_reset
  self.password_reset_token = SecureRandom.uuid
  self.password_reset_sent_at = Time.now
  save!(validate: false)

  Mailer.delay.password_reset(self)
end

I'm using Rails with locales en and fr-CA. If you need to see more code or need more info, just ask!

Thank you!

Most helpful comment

You probably need to set the locale per-job in Sidekiq, just like it is set per-request in Rails. Read this: http://guides.rubyonrails.org/i18n.html#setting-and-passing-the-locale Might be a great example of a middleware that someone could write.

All 10 comments

You probably need to set the locale per-job in Sidekiq, just like it is set per-request in Rails. Read this: http://guides.rubyonrails.org/i18n.html#setting-and-passing-the-locale Might be a great example of a middleware that someone could write.

Oh, I might tackle that! Do you have any great exemple of middleware that I could take a look at to get me started?

Thanks

I would take a look at the middleware that sidekiq ships with.

On 2 Mar 2013, at 16:25, Mathieu Allaire [email protected] wrote:

Oh, I might tackle that! Do you have any great exemple of middleware that I could take a look at to get me started?

Thanks

—
Reply to this email directly or view it on GitHub.

Specifically you'll want to do something like this:

# This gets the current locale and stores it in the message
# to be sent to Sidekiq
class AcmeCorp::Sidekiq::I18n::Client
  def call(worker_class, msg, queue)
    msg['locale'] = I18n.locale
    yield
  end
end

# Pulls the msg locale out and sets the current thread to use it.
class AcmeCorp::Sidekiq::I18n::Server
  def call(worker, msg, queue)
    I18n.locale = msg['locale'] || I18n.default_locale
    yield
  ensure
    I18n.locale = nil
  end
end

then you would install those middleware in your Sidekiq initializer:

Sidekiq.configure_client do |config|
  config.client_middleware do |chain|
    chain.add AcmeCorp::Sidekiq::I18n::Client
  end
end
Sidekiq.configure_server do |config|
  config.client_middleware do |chain|
    chain.add AcmeCorp::Sidekiq::I18n::Client
  end
  config.server_middleware do |chain|
    chain.add AcmeCorp::Sidekiq::I18n::Server
  end
end

That should do the trick.

Thank you sir, will take a look at this tomorrow!

This turned out to works great! bellow is my initializer. If you have a minute, I have a couple questions:

  • Should we add this i18n middleware to sidekiq? Maybe not loading it by default, but give the option to do so?
  • I noticed that you often define your Middlewares in the Middleware page or above in this issue using the resolution operator (i.e AcmeCorp::Sidekiq::I18n::Server), is it just to avoid typing all the module declaration extra lines? Because of course you can't define an undeclared module or class with the scope operator, might be confusing for some people?
  • Finally, in our case, is it useful to also have the config.client_middleware in the Sidekiq.configure_server block? I saw the little note on the Middleware page, but struggle to understand how a worker running in the Sidekiq server could act as a client (Still learning Sidekiq every day heh)

Thanks for all your work by the way, big fan.

    # This gets the current locale and stores it in the message
    # to be sent to Sidekiq
    module Lodgem
      module Sidekiq
        module Middleware
          module Client
            class I18n
              def call(worker_class, msg, queue)
                msg['locale'] = ::I18n.locale
                yield
              end
            end
          end
        end
      end
    end

    # Pulls the msg locale out and sets the current thread to use it.
    module Lodgem
      module Sidekiq
        module Middleware
          module Server
            class I18n
              def call(worker_instance, msg, queue)
                ::I18n.locale = msg['locale'] || ::I18n.default_locale
                yield

                ensure
                  ::I18n.locale = nil
              end
            end
          end
        end
      end
    end

    Sidekiq.configure_client do |config|
      config.client_middleware do |chain|
        chain.add Lodgem::Sidekiq::Middleware::Client::I18n
      end
    end

    Sidekiq.configure_server do |config|
      config.client_middleware do |chain|
        chain.add Lodgem::Sidekiq::Middleware::Client::I18n
      end
      config.server_middleware do |chain|
        chain.add Lodgem::Sidekiq::Middleware::Server::I18n
      end
    end

Yeah I'll put it in the 2.8.0 release.

On 3 Mar 2013, at 13:16, Mathieu Allaire [email protected] wrote:

This turned out to works great! bellow is my initializer. If you have a minute, I have a couple questions:

Should we add this i18n middleware to sidekiq? Maybe not loading it by default, but give the option to do so?
I noticed that you often define your Middlewares in the Middleware page or above in this issue using the resolution operator (i.e AcmeCorp::Sidekiq::I18n::Server), is it just to avoid typing all the module declaration extra lines? Because of course you can't define an undeclared module or class with the scope operator, might be confusing for some people?
Finally, in our case, is it useful to also have the config.client_middleware in the Sidekiq.configure_server block? I saw the little note in the Middleware page, but struggle to understand how a worker running in the Sidekiq server could act as a client (Still learning Sidekiq every day heh)
Thanks for all your work by the way, big fan.

# This gets the current locale and stores it in the message
# to be sent to Sidekiq
module Lodgem
  module Sidekiq
    module Middleware
      module Client
        class I18n
          def call(worker_class, msg, queue)
            msg['locale'] = ::I18n.locale
            yield
          end
        end
      end
    end
  end
end

# Pulls the msg locale out and sets the current thread to use it.
module Lodgem
  module Sidekiq
    module Middleware
      module Server
        class I18n
          def call(worker_instance, msg, queue)
            ::I18n.locale = msg['locale'] || ::I18n.default_locale
            yield

            ensure
              ::I18n.locale = nil
          end
        end
      end
    end
  end
end

Sidekiq.configure_client do |config|
  config.client_middleware do |chain|
    chain.add Lodgem::Sidekiq::Middleware::Client::I18n
  end
end

Sidekiq.configure_server do |config|
  config.client_middleware do |chain|
    chain.add Lodgem::Sidekiq::Middleware::Client::I18n
  end
  config.server_middleware do |chain|
    chain.add Lodgem::Sidekiq::Middleware::Server::I18n
  end
end

—
Reply to this email directly or view it on GitHub.

in case someone wonders how to use this. in your config:

require "sidekiq/middleware/i18n"

Hey @Goltergaul, how to require in config? Do you mean in initialisers?

@rvsingh that was very long ago... i suppose in sidekiq config which would be in an initializer

Was this page helpful?
0 / 5 - 0 ratings