Opened 2 years ago

Last modified 2 years ago

#21408 new Bug

Fallback to timesince produces erroneous translations in naturaltime

Reported by: 676c7473@… Owned by: nobody
Component: Internationalization Version: master
Severity: Normal Keywords: i18n l10n translation
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no


The naturaltime filter in django.contrib.humanize composes strings for days, weeks, months, and years from the words "ago" and "in" and the output of the built-in timesince filter. See here, where delta is to be supplied by timesince:

#: templatetags/
#, python-format
msgctxt "naturaltime"
msgid "%(delta)s ago"
msgstr "vor %(delta)s"

This produces erroneous translations in German (and probably other inflectional languages, too).

"3 days, 12 hours ago" is translated by Django as

vor 3 Tage, 12 Stunden

but the correct translation is (with dative inflection)

vor 3 Tagen, 12 Stunden

"3 Tage" is correct for timesince, but for naturaltime it must become "3 Tagen". Unfortunately, the assumption that these translations can simply be pieced together from translated bits in is mistaken.

I apologise if this isn't the right channel. I'd be glad to help, but so far I haven't seen a good way to avoid duplicating translations.

Change History (5)

comment:1 Changed 2 years ago by claudep

  • Component changed from Translations to Internationalization
  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Triage Stage changed from Unreviewed to Accepted
  • Version changed from 1.6 to master

Surely, this will need duplication of translatable strings. One solution would be to make the django.utils.timesince utility to accept a new optional argument containing a set of translatable strings. Then, naturaltime will be able to provide its own set of npgettext_lazy (with context) strings.

comment:2 Changed 2 years ago by 676c7473@…

Thank you for your answer. Slightly off-topic: I noticed a minor error in the docs for npgettext_lazy and have fixed it in

comment:3 Changed 2 years ago by 676c7473@…

I'm running into some difficulties with the code and the translations. Could it be that the .po files are out of sync with the code?

in, line 204, there are bits like this one:

    return ungettext(
        # Translators: \\u00a0 is non-breaking space
        'a second ago', '%(count)s\u00a0seconds ago', delta.seconds
    ) % {'count': delta.seconds}

There is a corresponding entry in the "en" locale, but in the "de" locale it looks different:

#. Translators: \\u00a0 is non-breaking space
#: templatetags/
#, python-format
msgid "a second ago"
msgid_plural "%(count)s\\u00a0seconds ago"
msgstr[0] ""
msgstr[1] ""
#: templatetags/
#, python-format
msgid "a second ago"
msgid_plural "%(count)s seconds ago"
msgstr[0] "vor einer Sekunde"
msgstr[1] "vor %(count)s Sekunden"

I guess the translation is still found because the msgid is the same, but the German translation doesn't have the non-breaking space.

Also, these gettext messages in, line 24,

    chunks = (
        (60 * 60 * 24 * 365, ungettext_lazy('%d year', '%d years')),
        (60 * 60 * 24 * 30, ungettext_lazy('%d month', '%d months')),

don't exist in the "de" translations. As far as I can tell, the msgid "year" will not match "%d year".

#: utils/
#, python-format
msgid "%d year"
msgid_plural "%d years"
msgstr[0] ""
msgstr[1] ""
#: utils/
msgid "year"
msgid_plural "years"
msgstr[0] "Jahr"
msgstr[1] "Jahre"

I'm not sure what's happening here. I'll experiment some more, maybe things will become clearer soon. If you want to share some advice, please do! Thanks.

comment:4 Changed 2 years ago by claudep

You might find more up-to-date translations in the 1.6.x branch. We've still not updated master with the more recent translations.

comment:5 Changed 2 years ago by 676c7473@…

Yes, @claudep, thank you, unfortunately there are no unit tests for those, and consequently the bug they introduced was overlooked: ticket:21415 Sorry for not noticing this earlier.


Note: See TracTickets for help on using tickets.
Back to Top