Sidekiq / sending Email digests fails: MissingTranslationData

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.

2 Likes

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)

https://github.com/discourse/discourse/blob/master/config/locales/server.en.yml#L23-L33

https://github.com/discourse/discourse/blob/master/config/locales/server.en.yml#L389-L460

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.

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?

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.

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.

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.

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

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

3 Likes
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.

2 Likes

Cool, fix was merged in