Opened 5 years ago

Closed 3 months ago

#21408 closed Bug (fixed)

Fallback to timesince produces erroneous translations in naturaltime

Reported by: 676c7473@… Owned by: Maximilian Merz
Component: Internationalization Version: master
Severity: Normal Keywords: i18n l10n translation
Cc: Triage Stage: Ready for checkin
Has patch: yes 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 (12)

comment:1 Changed 5 years ago by Claude Paroz

Component: TranslationsInternationalization
Triage Stage: UnreviewedAccepted
Version: 1.6master

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 5 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 5 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 5 years ago by Claude Paroz

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 5 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.


comment:6 Changed 18 months ago by Markus Bertheau

Bug still exists in 1.8.

A workaround for German for intervals into the future is to manually replace 'in' with 'noch', resulting in 'noch 2 Tage, 3 Stunden', which is correct.

comment:7 Changed 5 months ago by Maximilian Merz

Owner: changed from nobody to Maximilian Merz
Status: newassigned

comment:8 Changed 5 months ago by Maximilian Merz

comment:9 Changed 5 months ago by Tim Graham

Has patch: set

comment:10 Changed 4 months ago by Claude Paroz

Patch needs improvement: set

comment:11 Changed 3 months ago by Carlton Gibson

Patch needs improvement: unset
Triage Stage: AcceptedReady for checkin

comment:12 Changed 3 months ago by Claude Paroz <claude@…>

Resolution: fixed
Status: assignedclosed

In 78912ccd:

Fixed #21408 — German Translation for “3 days ago”

The problem:
“3 days ago” should translate to “vor 3 Tagen” in German, while “3 days” translates to “3 Tage”. #21408 describes that django always translated to “Tage”, even when the dative “Tagen” was correct. The same applies to months (“Monate”/“Monaten”) and years (“Jahre”/“Jahren”).

The solution:
Let timesince caller provide the string dict to use for the time-related strings.

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