Sidekiq / sending Email digests fails: MissingTranslationData


(Silver Quettier) #1

Sidekiq fails to send digest emails due to some missing translation data.

Here is the error, and the stacktrace:

Jobs::HandledExceptionWrapper: Wrapped I18n::MissingTranslationData: translation missing: en.time.formats.short

/var/www/discourse/vendor/bundle/ruby/2.0.0/gems/i18n-0.7.0/lib/i18n.rb:311:in `handle_exception'
/var/www/discourse/vendor/bundle/ruby/2.0.0/gems/i18n-0.7.0/lib/i18n.rb:161:in `translate'
/var/www/discourse/lib/freedom_patches/translate_accelerator.rb:64:in `translate'
/var/www/discourse/vendor/bundle/ruby/2.0.0/gems/i18n-0.7.0/lib/i18n/backend/base.rb:59:in `localize'
/var/www/discourse/vendor/bundle/ruby/2.0.0/gems/i18n-0.7.0/lib/i18n.rb:247:in `localize'
/var/www/discourse/app/mailers/user_notifications.rb:48:in `rescue in short_date'
/var/www/discourse/app/mailers/user_notifications.rb:46:in `short_date'
/var/www/discourse/app/mailers/user_notifications.rb:60:in `digest'
/var/www/discourse/vendor/bundle/ruby/2.0.0/gems/actionpack-4.1.10/lib/abstract_controller/base.rb:189:in `process_action'
/var/www/discourse/vendor/bundle/ruby/2.0.0/gems/actionpack-4.1.10/lib/abstract_controller/callbacks.rb:20:in `block in process_action'
/var/www/discourse/vendor/bundle/ruby/2.0.0/gems/activesupport-4.1.10/lib/active_support/callbacks.rb:82:in `run_callbacks'
/var/www/discourse/vendor/bundle/ruby/2.0.0/gems/actionpack-4.1.10/lib/abstract_controller/callbacks.rb:19:in `process_action'
/var/www/discourse/vendor/bundle/ruby/2.0.0/gems/actionpack-4.1.10/lib/abstract_controller/base.rb:136:in `process'
/var/www/discourse/vendor/bundle/ruby/2.0.0/gems/actionview-4.1.10/lib/action_view/rendering.rb:30:in `process'
/var/www/discourse/vendor/bundle/ruby/2.0.0/gems/actionmailer-4.1.10/lib/action_mailer/base.rb:580:in `block in process'
/var/www/discourse/vendor/bundle/ruby/2.0.0/gems/activesupport-4.1.10/lib/active_support/notifications.rb:159:in `block in instrument'

The forum is using the fr locale by default, and runs the latest stable version (1.3.2).

I dug through the code, and the offending blocks seems odd:

In user_notifications.rb

def short_date(dt)
    I18n.l(dt, format: :short)
  rescue I18n::MissingTranslationData
    I18n.l(dt, format: :short, locale: 'en')
  end

I’m no Ruby expert, and had trouble making sense of what I saw going deep into the rabbit hole of the ActiveSupport gem. Still, this part of the config/locales/server.en.yml file means that the fallback should work, at least:

  datetime: &datetime
    month_names:
      [~, January, February, March, April, May, June, July, August, September, October, November, December]
    formats:
      short: "%m-%d-%Y"
      short_no_year: "%B %-d"
      date_only: "%B %-d, %Y"
  date:
    <<: *datetime
  time:
    <<: *datetime

What is going wrong? I don’t get it.


(Gerhard Schlager) #2

I’ve seen this too - it’s still happening in master. If I recall correctly my conclusion was that a duplicate entry in the locale file is causing this. But debugging i18n issues is kinda annoying at the moment and I didn’t have enough time to verify my bugfix. (@sam What’s your take on this: Always enable localization fallback)

I guess they should be merged. Currently Transifex sees only the second usage of datetime. If those keys are merged it will make the month_names translatable.


(Silver Quettier) #3

Thank you @gerhard for your insight. I gave a try to your solution, and also gave a try to replacing the time alias by duplicate values, just in case it would be the problem. I ended up with a config/locales/server.en.yml file looking like this: (truncated)

  datetime: &datetime
    month_names:
      [~, January, February, March, April, May, June, July, August, September, October, November, December]
    formats:
      short: "%m-%d-%Y"
      short_no_year: "%B %-d"
      date_only: "%B %-d, %Y"
      
    distance_in_words:
      half_a_minute: "< 1m"
      less_than_x_seconds:
        one:   "< 1s"
        other: "< %{count}s"
      x_seconds:
        one:   "1s"
        other: "%{count}s"
      less_than_x_minutes:
        one:   "< 1m"
        other: "< %{count}m"
      x_minutes:
        one:   "1m"
        other: "%{count}m"
      about_x_hours:
        one:   "1h"
        other: "%{count}h"
      x_days:
        one:   "1d"
        other: "%{count}d"
      about_x_months:
        one:   "1mon"
        other: "%{count}mon"
      x_months:
        one:   "1mon"
        other: "%{count}mon"
      about_x_years:
        one:   "1y"
        other: "%{count}y"
      over_x_years:
        one:   "> 1y"
        other: "> %{count}y"
      almost_x_years:
        one:   "1y"
        other: "%{count}y"

    distance_in_words_verbose:
      half_a_minute: "just now"
      less_than_x_seconds:
        one:   "just now"
        other: "just now"
      x_seconds:
        one:   "1 second ago"
        other: "%{count} seconds ago"
      less_than_x_minutes:
        one:   "less than 1 minute ago"
        other: "less than %{count} minutes ago"
      x_minutes:
        one:   "1 minute ago"
        other: "%{count} minutes ago"
      about_x_hours:
        one:   "1 hour ago"
        other: "%{count} hours ago"
      x_days:
        one:   "1 day ago"
        other: "%{count} days ago"
      about_x_months:
        one:   "about 1 month ago"
        other: "about %{count} months ago"
      x_months:
        one:   "1 month ago"
        other: "%{count} months ago"
      about_x_years:
        one:   "about 1 year ago"
        other: "about %{count} years ago"
      over_x_years:
        one:   "over 1 year ago"
        other: "over %{count} years ago"
      almost_x_years:
        one:   "almost 1 year ago"
        other: "almost %{count} years ago"
  date:
    <<: *datetime
  time:
    month_names:
      [~, January, February, March, April, May, June, July, August, September, October, November, December]
    formats:
      short: "%m-%d-%Y"
      short_no_year: "%B %-d"
      date_only: "%B %-d, %Y"

Sadly, it did not solve the problem, and both removing the duplicate datetime entires and copying the datetime values to time did not solve the problem…

Any other ideas or pointers?


(Julien) #4

Same problem here since a while :

Jobs::HandledExceptionWrapper: Wrapped I18n::MissingTranslationData: translation missing: en.time.formats.short

Seems related to Email failed on V1.2.3

Upgrading to v1.3.5 and then to v1.4.0.beta9 does not fix this issue.


(Gerhard Schlager) #5

Really? You are seeing this in v1.4.0.beta9?
I checked this last week on the master branch and it worked. Well, at least it worked in production mode. It definitely doesn’t work in development mode.


(Julien) #6

This one just work in rails console (production env) :
Jobs::UserEmail.new.execute(type: “digest”, user_id: …, current_site_id: “default”)

But it does not work through Sidekiq, even after a restart. I do not understand why.

Edit: deliverability test works well.


(Gerhard Schlager) #7

Oh, I see. I didn’t test it with Sidekiq. I just used admin interface for previewing the digest mail.
Maybe the locale fallback doesn’t work inside Sidekiq? cc @riking


(Gerhard Schlager) #8

OK, I can confirm this bug still exists in master. The digest preview works, but the actual Sidekiq job fails with:

Jobs::HandledExceptionWrapper: Wrapped I18n::MissingTranslationData: translation missing: de.time.formats.short

Somehow the locale fallback doesn’t work in this case.
This is quite a severe bug since it’s not possible to send digest mails when those translations are missing.

@sam I wanted to find out what’s causing this, but I couldn’t figure out how to debug Sidekiq jobs. Any hints in case I need to do this again? I tried following the steps mentioned on stackoverflow, but my Sidekiq instance completly ignored all enqueued jobs. :frowning:

Update: I guess we need to call I18n.fallbacks.ensure_loaded! somewhere. It’s currently only called in application_controller.rb.

Update 2: I sent a PR which should fix this:
https://github.com/discourse/discourse/pull/3786


(Julien) #9
diff --git a/app/jobs/regular/user_email.rb b/app/jobs/regular/user_email.rb
index 0ef4811..acb247e 100644
--- a/app/jobs/regular/user_email.rb
+++ b/app/jobs/regular/user_email.rb
@@ -56,6 +56,7 @@ module Jobs
       # Make sure that mailer exists
       raise Discourse::InvalidParameters.new(:type) unless UserNotifications.respond_to?(args[:type])
 
+      I18n.fallbacks.ensure_loaded!
       message = UserNotifications.send(args[:type], @user, email_args)
       # Update the to address if we have a custom one
       if args[:to_address].present?

It fixed my problem. I’ll drop my hotfix, thank for your commit! For information, last digest my instance sent was march 18, 2015.


(Sam Saffron) #10

Cool, fix was merged in


(Sam Saffron) #11