mirror of
				https://github.com/django/django.git
				synced 2025-10-30 17:16:10 +00:00 
			
		
		
		
	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.
This commit is contained in:
		
				
					committed by
					
						 Claude Paroz
						Claude Paroz
					
				
			
			
				
	
			
			
			
						parent
						
							704443acac
						
					
				
				
					commit
					78912ccd0e
				
			
							
								
								
									
										1
									
								
								AUTHORS
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								AUTHORS
									
									
									
									
									
								
							| @@ -559,6 +559,7 @@ answer newbie questions, and generally made Django that much better: | ||||
|     Max Derkachev <mderk@yandex.ru> | ||||
|     Maxime Lorant <maxime.lorant@gmail.com> | ||||
|     Maxime Turcotte <maxocub@riseup.net> | ||||
|     Maximilian Merz <django@mxmerz.de> | ||||
|     Maximillian Dornseif <md@hudora.de> | ||||
|     mccutchen@gmail.com | ||||
|     Meir Kriheli <http://mksoft.co.il/> | ||||
|   | ||||
										
											Binary file not shown.
										
									
								
							| @@ -4,12 +4,13 @@ | ||||
| # André Hagenbruch, 2011 | ||||
| # Claude Paroz <claude@2xlibre.net>, 2013 | ||||
| # Jannis Leidel <jannis@leidel.info>, 2011,2013-2014 | ||||
| # Maximilian Merz <django@mxmerz.de>, 2018 | ||||
| msgid "" | ||||
| msgstr "" | ||||
| "Project-Id-Version: django\n" | ||||
| "Report-Msgid-Bugs-To: \n" | ||||
| "POT-Creation-Date: 2015-01-17 11:07+0100\n" | ||||
| "PO-Revision-Date: 2017-09-23 18:54+0000\n" | ||||
| "PO-Revision-Date: 2018-03-13 16:07+0100\n" | ||||
| "Last-Translator: Jannis Leidel <jannis@leidel.info>\n" | ||||
| "Language-Team: German (http://www.transifex.com/django/django/language/de/)\n" | ||||
| "MIME-Version: 1.0\n" | ||||
| @@ -202,7 +203,6 @@ msgid "yesterday" | ||||
| msgstr "gestern" | ||||
|  | ||||
| #, python-format | ||||
| msgctxt "naturaltime" | ||||
| msgid "%(delta)s ago" | ||||
| msgstr "vor %(delta)s" | ||||
|  | ||||
| @@ -234,10 +234,56 @@ msgstr[0] "vor einer Stunde" | ||||
| msgstr[1] "vor %(count)s Stunden" | ||||
|  | ||||
| #, python-format | ||||
| msgctxt "naturaltime" | ||||
| msgid "%(delta)s from now" | ||||
| msgstr "in %(delta)s" | ||||
|  | ||||
| #, python-format | ||||
| msgctxt "naturaltime-future" | ||||
| msgid "%(delta)s from now" | ||||
| msgstr "in %(delta)s" | ||||
|  | ||||
| #, python-format | ||||
| msgctxt "naturaltime-past" | ||||
| msgid "%d day" | ||||
| msgid_plural "%d days" | ||||
| msgstr[0] "%d Tag" | ||||
| msgstr[1] "%d Tagen" | ||||
|  | ||||
| #, python-format | ||||
| msgctxt "naturaltime-past" | ||||
| msgid "%d month" | ||||
| msgid_plural "%d months" | ||||
| msgstr[0] "%d Monat" | ||||
| msgstr[1] "%d Monaten" | ||||
|  | ||||
| #, python-format | ||||
| msgctxt "naturaltime-past" | ||||
| msgid "%d year" | ||||
| msgid_plural "%d years" | ||||
| msgstr[0] "%d Jahr" | ||||
| msgstr[1] "%d Jahren" | ||||
|  | ||||
| #, python-format | ||||
| msgctxt "naturaltime-future" | ||||
| msgid "%d day" | ||||
| msgid_plural "%d days" | ||||
| msgstr[0] "%d Tag" | ||||
| msgstr[1] "%d Tagen" | ||||
|  | ||||
| #, python-format | ||||
| msgctxt "naturaltime-future" | ||||
| msgid "%d month" | ||||
| msgid_plural "%d months" | ||||
| msgstr[0] "%d Monat" | ||||
| msgstr[1] "%d Monaten" | ||||
|  | ||||
| #, python-format | ||||
| msgctxt "naturaltime-future" | ||||
| msgid "%d year" | ||||
| msgid_plural "%d years" | ||||
| msgstr[0] "%d Jahr" | ||||
| msgstr[1] "%d Jahren" | ||||
|  | ||||
| #. Translators: please keep a non-breaking space (U+00A0) | ||||
| #. between count and time unit. | ||||
| #, python-format | ||||
|   | ||||
| @@ -8,7 +8,9 @@ from django.template import defaultfilters | ||||
| from django.utils.formats import number_format | ||||
| from django.utils.safestring import mark_safe | ||||
| from django.utils.timezone import is_aware, utc | ||||
| from django.utils.translation import gettext as _, ngettext, pgettext | ||||
| from django.utils.translation import ( | ||||
|     gettext as _, ngettext, npgettext_lazy, pgettext, | ||||
| ) | ||||
|  | ||||
| register = template.Library() | ||||
|  | ||||
| @@ -220,7 +222,16 @@ def naturaltime(value): | ||||
|         delta = now - value | ||||
|         if delta.days != 0: | ||||
|             # Translators: delta will contain a string like '2 months' or '1 month, 2 weeks' | ||||
|             return _('%(delta)s ago') % {'delta': defaultfilters.timesince(value, now)} | ||||
|             return _('%(delta)s ago') % {'delta': defaultfilters.timesince(value, now, time_strings={ | ||||
|                 # Translators: 'naturaltime-past' strings will be included in | ||||
|                 # '%(delta)s ago' | ||||
|                 'year': npgettext_lazy('naturaltime-past', '%d year', '%d years'), | ||||
|                 'month': npgettext_lazy('naturaltime-past', '%d month', '%d months'), | ||||
|                 'week': npgettext_lazy('naturaltime-past', '%d week', '%d weeks'), | ||||
|                 'day': npgettext_lazy('naturaltime-past', '%d day', '%d days'), | ||||
|                 'hour': npgettext_lazy('naturaltime-past', '%d hour', '%d hours'), | ||||
|                 'minute': npgettext_lazy('naturaltime-past', '%d minute', '%d minutes') | ||||
|             })} | ||||
|         elif delta.seconds == 0: | ||||
|             return _('now') | ||||
|         elif delta.seconds < 60: | ||||
| @@ -247,7 +258,16 @@ def naturaltime(value): | ||||
|         delta = value - now | ||||
|         if delta.days != 0: | ||||
|             # Translators: delta will contain a string like '2 months' or '1 month, 2 weeks' | ||||
|             return _('%(delta)s from now') % {'delta': defaultfilters.timeuntil(value, now)} | ||||
|             return _('%(delta)s from now') % {'delta': defaultfilters.timeuntil(value, now, time_strings={ | ||||
|                 # Translators: 'naturaltime-future' strings will be included in | ||||
|                 # '%(delta)s from now' | ||||
|                 'year': npgettext_lazy('naturaltime-future', '%d year', '%d years'), | ||||
|                 'month': npgettext_lazy('naturaltime-future', '%d month', '%d months'), | ||||
|                 'week': npgettext_lazy('naturaltime-future', '%d week', '%d weeks'), | ||||
|                 'day': npgettext_lazy('naturaltime-future', '%d day', '%d days'), | ||||
|                 'hour': npgettext_lazy('naturaltime-future', '%d hour', '%d hours'), | ||||
|                 'minute': npgettext_lazy('naturaltime-future', '%d minute', '%d minutes') | ||||
|             })} | ||||
|         elif delta.seconds == 0: | ||||
|             return _('now') | ||||
|         elif delta.seconds < 60: | ||||
|   | ||||
| @@ -5,17 +5,26 @@ from django.utils.html import avoid_wrapping | ||||
| from django.utils.timezone import is_aware, utc | ||||
| from django.utils.translation import gettext, ngettext_lazy | ||||
|  | ||||
| TIME_STRINGS = { | ||||
|     'year': ngettext_lazy('%d year', '%d years'), | ||||
|     'month': ngettext_lazy('%d month', '%d months'), | ||||
|     'week': ngettext_lazy('%d week', '%d weeks'), | ||||
|     'day': ngettext_lazy('%d day', '%d days'), | ||||
|     'hour': ngettext_lazy('%d hour', '%d hours'), | ||||
|     'minute': ngettext_lazy('%d minute', '%d minutes'), | ||||
| } | ||||
|  | ||||
| TIMESINCE_CHUNKS = ( | ||||
|     (60 * 60 * 24 * 365, ngettext_lazy('%d year', '%d years')), | ||||
|     (60 * 60 * 24 * 30, ngettext_lazy('%d month', '%d months')), | ||||
|     (60 * 60 * 24 * 7, ngettext_lazy('%d week', '%d weeks')), | ||||
|     (60 * 60 * 24, ngettext_lazy('%d day', '%d days')), | ||||
|     (60 * 60, ngettext_lazy('%d hour', '%d hours')), | ||||
|     (60, ngettext_lazy('%d minute', '%d minutes')) | ||||
|     (60 * 60 * 24 * 365, 'year'), | ||||
|     (60 * 60 * 24 * 30, 'month'), | ||||
|     (60 * 60 * 24 * 7, 'week'), | ||||
|     (60 * 60 * 24, 'day'), | ||||
|     (60 * 60, 'hour'), | ||||
|     (60, 'minute'), | ||||
| ) | ||||
|  | ||||
|  | ||||
| def timesince(d, now=None, reversed=False): | ||||
| def timesince(d, now=None, reversed=False, time_strings=None): | ||||
|     """ | ||||
|     Take two datetime objects and return the time between d and now as a nicely | ||||
|     formatted string, e.g. "10 minutes". If d occurs after now, return | ||||
| @@ -26,9 +35,15 @@ def timesince(d, now=None, reversed=False): | ||||
|     displayed.  For example, "2 weeks, 3 days" and "1 year, 3 months" are | ||||
|     possible outputs, but "2 weeks, 3 hours" and "1 year, 5 days" are not. | ||||
|  | ||||
|     `time_strings` is an optional dict of strings to replace the default | ||||
|     TIME_STRINGS dict. | ||||
|  | ||||
|     Adapted from | ||||
|     http://web.archive.org/web/20060617175230/http://blog.natbat.co.uk/archive/2003/Jun/14/time_since | ||||
|     """ | ||||
|     if time_strings is None: | ||||
|         time_strings = TIME_STRINGS | ||||
|  | ||||
|     # Convert datetime.date to datetime.datetime for comparison. | ||||
|     if not isinstance(d, datetime.datetime): | ||||
|         d = datetime.datetime(d.year, d.month, d.day) | ||||
| @@ -59,18 +74,18 @@ def timesince(d, now=None, reversed=False): | ||||
|         count = since // seconds | ||||
|         if count != 0: | ||||
|             break | ||||
|     result = avoid_wrapping(name % count) | ||||
|     result = avoid_wrapping(time_strings[name] % count) | ||||
|     if i + 1 < len(TIMESINCE_CHUNKS): | ||||
|         # Now get the second item | ||||
|         seconds2, name2 = TIMESINCE_CHUNKS[i + 1] | ||||
|         count2 = (since - (seconds * count)) // seconds2 | ||||
|         if count2 != 0: | ||||
|             result += gettext(', ') + avoid_wrapping(name2 % count2) | ||||
|             result += gettext(', ') + avoid_wrapping(time_strings[name2] % count2) | ||||
|     return result | ||||
|  | ||||
|  | ||||
| def timeuntil(d, now=None): | ||||
| def timeuntil(d, now=None, time_strings=None): | ||||
|     """ | ||||
|     Like timesince, but return a string measuring the time until the given time. | ||||
|     """ | ||||
|     return timesince(d, now, reversed=True) | ||||
|     return timesince(d, now, reversed=True, time_strings=time_strings) | ||||
|   | ||||
| @@ -289,3 +289,29 @@ class HumanizeTests(SimpleTestCase): | ||||
|                 self.assertEqual(expected_natural_time, natural_time) | ||||
|         finally: | ||||
|             humanize.datetime = orig_humanize_datetime | ||||
|  | ||||
|     def test_dative_inflection_for_timedelta(self): | ||||
|         """Translation may differ depending on the string it is inserted in.""" | ||||
|         test_list = [ | ||||
|             now - datetime.timedelta(days=1), | ||||
|             now - datetime.timedelta(days=2), | ||||
|             now - datetime.timedelta(days=30), | ||||
|             now - datetime.timedelta(days=60), | ||||
|             now - datetime.timedelta(days=500), | ||||
|             now - datetime.timedelta(days=865), | ||||
|         ] | ||||
|         result_list = [ | ||||
|             'vor 1\xa0Tag', | ||||
|             'vor 2\xa0Tagen', | ||||
|             'vor 1\xa0Monat', | ||||
|             'vor 2\xa0Monaten', | ||||
|             'vor 1\xa0Jahr, 4\xa0Monaten', | ||||
|             'vor 2\xa0Jahren, 4\xa0Monaten', | ||||
|         ] | ||||
|  | ||||
|         orig_humanize_datetime, humanize.datetime = humanize.datetime, MockDateTime | ||||
|         try: | ||||
|             with translation.override('de'), self.settings(USE_L10N=True): | ||||
|                 self.humanize_tester(test_list, result_list, 'naturaltime') | ||||
|         finally: | ||||
|             humanize.datetime = orig_humanize_datetime | ||||
|   | ||||
		Reference in New Issue
	
	Block a user