Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #20246 -- Added non-breaking spaces between values an units

  • Loading branch information...
commit 7d77e9786a118dd95a268872dd9d36664066b96a 1 parent caf56ad
Emil Stenström authored May 18, 2013 claudep committed May 18, 2013
1  AUTHORS
@@ -536,6 +536,7 @@ answer newbie questions, and generally made Django that much better:
536 536
     starrynight <cmorgh@gmail.com>
537 537
     Vasiliy Stavenko <stavenko@gmail.com>
538 538
     Thomas Steinacher <http://www.eggdrop.ch/>
  539
+    Emil Stenström <em@kth.se>
539 540
     Johan C. Stöver <johan@nilling.nl>
540 541
     Nowell Strite <http://nowell.strite.org/>
541 542
     Thomas Stromberg <tstromberg@google.com>
18  django/contrib/humanize/templatetags/humanize.py
@@ -194,17 +194,20 @@ def naturaltime(value):
194 194
             return _('now')
195 195
         elif delta.seconds < 60:
196 196
             return ungettext(
197  
-                'a second ago', '%(count)s seconds ago', delta.seconds
  197
+                # Translators: \\u00a0 is non-breaking space
  198
+                'a second ago', '%(count)s\u00a0seconds ago', delta.seconds
198 199
             ) % {'count': delta.seconds}
199 200
         elif delta.seconds // 60 < 60:
200 201
             count = delta.seconds // 60
201 202
             return ungettext(
202  
-                'a minute ago', '%(count)s minutes ago', count
  203
+                # Translators: \\u00a0 is non-breaking space
  204
+                'a minute ago', '%(count)s\u00a0minutes ago', count
203 205
             ) % {'count': count}
204 206
         else:
205 207
             count = delta.seconds // 60 // 60
206 208
             return ungettext(
207  
-                'an hour ago', '%(count)s hours ago', count
  209
+                # Translators: \\u00a0 is non-breaking space
  210
+                'an hour ago', '%(count)s\u00a0hours ago', count
208 211
             ) % {'count': count}
209 212
     else:
210 213
         delta = value - now
@@ -216,15 +219,18 @@ def naturaltime(value):
216 219
             return _('now')
217 220
         elif delta.seconds < 60:
218 221
             return ungettext(
219  
-                'a second from now', '%(count)s seconds from now', delta.seconds
  222
+                # Translators: \\u00a0 is non-breaking space
  223
+                'a second from now', '%(count)s\u00a0seconds from now', delta.seconds
220 224
             ) % {'count': delta.seconds}
221 225
         elif delta.seconds // 60 < 60:
222 226
             count = delta.seconds // 60
223 227
             return ungettext(
224  
-                'a minute from now', '%(count)s minutes from now', count
  228
+                # Translators: \\u00a0 is non-breaking space
  229
+                'a minute from now', '%(count)s\u00a0minutes from now', count
225 230
             ) % {'count': count}
226 231
         else:
227 232
             count = delta.seconds // 60 // 60
228 233
             return ungettext(
229  
-                'an hour from now', '%(count)s hours from now', count
  234
+                # Translators: \\u00a0 is non-breaking space
  235
+                'an hour from now', '%(count)s\u00a0hours from now', count
230 236
             ) % {'count': count}
26  django/contrib/humanize/tests.py
@@ -195,22 +195,22 @@ def utcoffset(self, dt):
195 195
         result_list = [
196 196
             'now',
197 197
             'a second ago',
198  
-            '30 seconds ago',
  198
+            '30\xa0seconds ago',
199 199
             'a minute ago',
200  
-            '2 minutes ago',
  200
+            '2\xa0minutes ago',
201 201
             'an hour ago',
202  
-            '23 hours ago',
203  
-            '1 day ago',
204  
-            '1 year, 4 months ago',
  202
+            '23\xa0hours ago',
  203
+            '1\xa0day ago',
  204
+            '1\xa0year, 4\xa0months ago',
205 205
             'a second from now',
206  
-            '30 seconds from now',
  206
+            '30\xa0seconds from now',
207 207
             'a minute from now',
208  
-            '2 minutes from now',
  208
+            '2\xa0minutes from now',
209 209
             'an hour from now',
210  
-            '23 hours from now',
211  
-            '1 day from now',
212  
-            '2 days, 6 hours from now',
213  
-            '1 year, 4 months from now',
  210
+            '23\xa0hours from now',
  211
+            '1\xa0day from now',
  212
+            '2\xa0days, 6\xa0hours from now',
  213
+            '1\xa0year, 4\xa0months from now',
214 214
             'now',
215 215
             'now',
216 216
         ]
@@ -218,8 +218,8 @@ def utcoffset(self, dt):
218 218
         # date in naive arithmetic is only 2 days and 5 hours after in
219 219
         # aware arithmetic.
220 220
         result_list_with_tz_support = result_list[:]
221  
-        assert result_list_with_tz_support[-4] == '2 days, 6 hours from now'
222  
-        result_list_with_tz_support[-4] == '2 days, 5 hours from now'
  221
+        assert result_list_with_tz_support[-4] == '2\xa0days, 6\xa0hours from now'
  222
+        result_list_with_tz_support[-4] == '2\xa0days, 5\xa0hours from now'
223 223
 
224 224
         orig_humanize_datetime, humanize.datetime = humanize.datetime, MockDateTime
225 225
         try:
28  django/template/defaultfilters.py
@@ -14,7 +14,7 @@
14 14
 from django.utils.dateformat import format, time_format
15 15
 from django.utils.encoding import force_text, iri_to_uri
16 16
 from django.utils.html import (conditional_escape, escapejs, fix_ampersands,
17  
-    escape, urlize as urlize_impl, linebreaks, strip_tags)
  17
+    escape, urlize as urlize_impl, linebreaks, strip_tags, avoid_wrapping)
18 18
 from django.utils.http import urlquote
19 19
 from django.utils.text import Truncator, wrap, phone2numeric
20 20
 from django.utils.safestring import mark_safe, SafeData, mark_for_escaping
@@ -810,7 +810,8 @@ def filesizeformat(bytes):
810 810
     try:
811 811
         bytes = float(bytes)
812 812
     except (TypeError,ValueError,UnicodeDecodeError):
813  
-        return ungettext("%(size)d byte", "%(size)d bytes", 0) % {'size': 0}
  813
+        value = ungettext("%(size)d byte", "%(size)d bytes", 0) % {'size': 0}
  814
+        return avoid_wrapping(value)
814 815
 
815 816
     filesize_number_format = lambda value: formats.number_format(round(value, 1), 1)
816 817
 
@@ -821,16 +822,19 @@ def filesizeformat(bytes):
821 822
     PB = 1<<50
822 823
 
823 824
     if bytes < KB:
824  
-        return ungettext("%(size)d byte", "%(size)d bytes", bytes) % {'size': bytes}
825  
-    if bytes < MB:
826  
-        return ugettext("%s KB") % filesize_number_format(bytes / KB)
827  
-    if bytes < GB:
828  
-        return ugettext("%s MB") % filesize_number_format(bytes / MB)
829  
-    if bytes < TB:
830  
-        return ugettext("%s GB") % filesize_number_format(bytes / GB)
831  
-    if bytes < PB:
832  
-        return ugettext("%s TB") % filesize_number_format(bytes / TB)
833  
-    return ugettext("%s PB") % filesize_number_format(bytes / PB)
  825
+        value = ungettext("%(size)d byte", "%(size)d bytes", bytes) % {'size': bytes}
  826
+    elif bytes < MB:
  827
+        value = ugettext("%s KB") % filesize_number_format(bytes / KB)
  828
+    elif bytes < GB:
  829
+        value = ugettext("%s MB") % filesize_number_format(bytes / MB)
  830
+    elif bytes < TB:
  831
+        value = ugettext("%s GB") % filesize_number_format(bytes / GB)
  832
+    elif bytes < PB:
  833
+        value = ugettext("%s TB") % filesize_number_format(bytes / TB)
  834
+    else:
  835
+        value = ugettext("%s PB") % filesize_number_format(bytes / PB)
  836
+
  837
+    return avoid_wrapping(value)
834 838
 
835 839
 @register.filter(is_safe=False)
836 840
 def pluralize(value, arg='s'):
7  django/utils/html.py
@@ -281,3 +281,10 @@ def replace_p_tags(match):
281 281
     text = trailing_empty_content_re.sub('', text)
282 282
     return text
283 283
 clean_html = allow_lazy(clean_html, six.text_type)
  284
+
  285
+def avoid_wrapping(value):
  286
+    """
  287
+    Avoid text wrapping in the middle of a phrase by adding non-breaking
  288
+    spaces where there previously were normal spaces.
  289
+    """
  290
+    return value.replace(" ", "\xa0")
7  django/utils/timesince.py
@@ -2,6 +2,7 @@
2 2
 
3 3
 import datetime
4 4
 
  5
+from django.utils.html import avoid_wrapping
5 6
 from django.utils.timezone import is_aware, utc
6 7
 from django.utils.translation import ugettext, ungettext_lazy
7 8
 
@@ -40,18 +41,18 @@ def timesince(d, now=None, reversed=False):
40 41
     since = delta.days * 24 * 60 * 60 + delta.seconds
41 42
     if since <= 0:
42 43
         # d is in the future compared to now, stop processing.
43  
-        return ugettext('0 minutes')
  44
+        return avoid_wrapping(ugettext('0 minutes'))
44 45
     for i, (seconds, name) in enumerate(chunks):
45 46
         count = since // seconds
46 47
         if count != 0:
47 48
             break
48  
-    result = name % count
  49
+    result = avoid_wrapping(name % count)
49 50
     if i + 1 < len(chunks):
50 51
         # Now get the second item
51 52
         seconds2, name2 = chunks[i + 1]
52 53
         count2 = (since - (seconds * count)) // seconds2
53 54
         if count2 != 0:
54  
-            result += ugettext(', ') + name2 % count2
  55
+            result += ugettext(', ') + avoid_wrapping(name2 % count2)
55 56
     return result
56 57
 
57 58
 def timeuntil(d, now=None):
68  tests/defaultfilters/tests.py
@@ -527,24 +527,26 @@ def test_time(self):
527 527
 
528 528
     def test_timesince(self):
529 529
         # real testing is done in timesince.py, where we can provide our own 'now'
  530
+        # NOTE: \xa0 avoids wrapping between value and unit
530 531
         self.assertEqual(
531 532
             timesince_filter(datetime.datetime.now() - datetime.timedelta(1)),
532  
-            '1 day')
  533
+            '1\xa0day')
533 534
 
534 535
         self.assertEqual(
535 536
             timesince_filter(datetime.datetime(2005, 12, 29),
536 537
                              datetime.datetime(2005, 12, 30)),
537  
-            '1 day')
  538
+            '1\xa0day')
538 539
 
539 540
     def test_timeuntil(self):
  541
+        # NOTE: \xa0 avoids wrapping between value and unit
540 542
         self.assertEqual(
541 543
             timeuntil_filter(datetime.datetime.now() + datetime.timedelta(1, 1)),
542  
-            '1 day')
  544
+            '1\xa0day')
543 545
 
544 546
         self.assertEqual(
545 547
             timeuntil_filter(datetime.datetime(2005, 12, 30),
546 548
                              datetime.datetime(2005, 12, 29)),
547  
-            '1 day')
  549
+            '1\xa0day')
548 550
 
549 551
     def test_default(self):
550 552
         self.assertEqual(default("val", "default"), 'val')
@@ -574,43 +576,45 @@ def test_yesno(self):
574 576
                           'get out of town')
575 577
 
576 578
     def test_filesizeformat(self):
577  
-        self.assertEqual(filesizeformat(1023), '1023 bytes')
578  
-        self.assertEqual(filesizeformat(1024), '1.0 KB')
579  
-        self.assertEqual(filesizeformat(10*1024), '10.0 KB')
580  
-        self.assertEqual(filesizeformat(1024*1024-1), '1024.0 KB')
581  
-        self.assertEqual(filesizeformat(1024*1024), '1.0 MB')
582  
-        self.assertEqual(filesizeformat(1024*1024*50), '50.0 MB')
583  
-        self.assertEqual(filesizeformat(1024*1024*1024-1), '1024.0 MB')
584  
-        self.assertEqual(filesizeformat(1024*1024*1024), '1.0 GB')
585  
-        self.assertEqual(filesizeformat(1024*1024*1024*1024), '1.0 TB')
586  
-        self.assertEqual(filesizeformat(1024*1024*1024*1024*1024), '1.0 PB')
  579
+        # NOTE: \xa0 avoids wrapping between value and unit
  580
+        self.assertEqual(filesizeformat(1023), '1023\xa0bytes')
  581
+        self.assertEqual(filesizeformat(1024), '1.0\xa0KB')
  582
+        self.assertEqual(filesizeformat(10*1024), '10.0\xa0KB')
  583
+        self.assertEqual(filesizeformat(1024*1024-1), '1024.0\xa0KB')
  584
+        self.assertEqual(filesizeformat(1024*1024), '1.0\xa0MB')
  585
+        self.assertEqual(filesizeformat(1024*1024*50), '50.0\xa0MB')
  586
+        self.assertEqual(filesizeformat(1024*1024*1024-1), '1024.0\xa0MB')
  587
+        self.assertEqual(filesizeformat(1024*1024*1024), '1.0\xa0GB')
  588
+        self.assertEqual(filesizeformat(1024*1024*1024*1024), '1.0\xa0TB')
  589
+        self.assertEqual(filesizeformat(1024*1024*1024*1024*1024), '1.0\xa0PB')
587 590
         self.assertEqual(filesizeformat(1024*1024*1024*1024*1024*2000),
588  
-                          '2000.0 PB')
589  
-        self.assertEqual(filesizeformat(complex(1,-1)), '0 bytes')
590  
-        self.assertEqual(filesizeformat(""), '0 bytes')
  591
+                          '2000.0\xa0PB')
  592
+        self.assertEqual(filesizeformat(complex(1,-1)), '0\xa0bytes')
  593
+        self.assertEqual(filesizeformat(""), '0\xa0bytes')
591 594
         self.assertEqual(filesizeformat("\N{GREEK SMALL LETTER ALPHA}"),
592  
-                          '0 bytes')
  595
+                          '0\xa0bytes')
593 596
 
594 597
     def test_localized_filesizeformat(self):
  598
+        # NOTE: \xa0 avoids wrapping between value and unit
595 599
         with self.settings(USE_L10N=True):
596 600
             with translation.override('de', deactivate=True):
597  
-                self.assertEqual(filesizeformat(1023), '1023 Bytes')
598  
-                self.assertEqual(filesizeformat(1024), '1,0 KB')
599  
-                self.assertEqual(filesizeformat(10*1024), '10,0 KB')
600  
-                self.assertEqual(filesizeformat(1024*1024-1), '1024,0 KB')
601  
-                self.assertEqual(filesizeformat(1024*1024), '1,0 MB')
602  
-                self.assertEqual(filesizeformat(1024*1024*50), '50,0 MB')
603  
-                self.assertEqual(filesizeformat(1024*1024*1024-1), '1024,0 MB')
604  
-                self.assertEqual(filesizeformat(1024*1024*1024), '1,0 GB')
605  
-                self.assertEqual(filesizeformat(1024*1024*1024*1024), '1,0 TB')
  601
+                self.assertEqual(filesizeformat(1023), '1023\xa0Bytes')
  602
+                self.assertEqual(filesizeformat(1024), '1,0\xa0KB')
  603
+                self.assertEqual(filesizeformat(10*1024), '10,0\xa0KB')
  604
+                self.assertEqual(filesizeformat(1024*1024-1), '1024,0\xa0KB')
  605
+                self.assertEqual(filesizeformat(1024*1024), '1,0\xa0MB')
  606
+                self.assertEqual(filesizeformat(1024*1024*50), '50,0\xa0MB')
  607
+                self.assertEqual(filesizeformat(1024*1024*1024-1), '1024,0\xa0MB')
  608
+                self.assertEqual(filesizeformat(1024*1024*1024), '1,0\xa0GB')
  609
+                self.assertEqual(filesizeformat(1024*1024*1024*1024), '1,0\xa0TB')
606 610
                 self.assertEqual(filesizeformat(1024*1024*1024*1024*1024),
607  
-                                  '1,0 PB')
  611
+                                  '1,0\xa0PB')
608 612
                 self.assertEqual(filesizeformat(1024*1024*1024*1024*1024*2000),
609  
-                                  '2000,0 PB')
610  
-                self.assertEqual(filesizeformat(complex(1,-1)), '0 Bytes')
611  
-                self.assertEqual(filesizeformat(""), '0 Bytes')
  613
+                                  '2000,0\xa0PB')
  614
+                self.assertEqual(filesizeformat(complex(1,-1)), '0\xa0Bytes')
  615
+                self.assertEqual(filesizeformat(""), '0\xa0Bytes')
612 616
                 self.assertEqual(filesizeformat("\N{GREEK SMALL LETTER ALPHA}"),
613  
-                                  '0 Bytes')
  617
+                                  '0\xa0Bytes')
614 618
 
615 619
     def test_pluralize(self):
616 620
         self.assertEqual(pluralize(1), '')
59  tests/template_tests/filters.py
@@ -35,59 +35,60 @@ def get_filter_tests():
35 35
     now_tz_i = datetime.now(FixedOffset((3 * 60) + 15)) # imaginary time zone
36 36
     today = date.today()
37 37
 
  38
+    # NOTE: \xa0 avoids wrapping between value and unit
38 39
     return {
39 40
         # Default compare with datetime.now()
40  
-        'filter-timesince01' : ('{{ a|timesince }}', {'a': datetime.now() + timedelta(minutes=-1, seconds = -10)}, '1 minute'),
41  
-        'filter-timesince02' : ('{{ a|timesince }}', {'a': datetime.now() - timedelta(days=1, minutes = 1)}, '1 day'),
42  
-        'filter-timesince03' : ('{{ a|timesince }}', {'a': datetime.now() - timedelta(hours=1, minutes=25, seconds = 10)}, '1 hour, 25 minutes'),
  41
+        'filter-timesince01' : ('{{ a|timesince }}', {'a': datetime.now() + timedelta(minutes=-1, seconds = -10)}, '1\xa0minute'),
  42
+        'filter-timesince02' : ('{{ a|timesince }}', {'a': datetime.now() - timedelta(days=1, minutes = 1)}, '1\xa0day'),
  43
+        'filter-timesince03' : ('{{ a|timesince }}', {'a': datetime.now() - timedelta(hours=1, minutes=25, seconds = 10)}, '1\xa0hour, 25\xa0minutes'),
43 44
 
44 45
         # Compare to a given parameter
45  
-        'filter-timesince04' : ('{{ a|timesince:b }}', {'a':now - timedelta(days=2), 'b':now - timedelta(days=1)}, '1 day'),
46  
-        'filter-timesince05' : ('{{ a|timesince:b }}', {'a':now - timedelta(days=2, minutes=1), 'b':now - timedelta(days=2)}, '1 minute'),
  46
+        'filter-timesince04' : ('{{ a|timesince:b }}', {'a':now - timedelta(days=2), 'b':now - timedelta(days=1)}, '1\xa0day'),
  47
+        'filter-timesince05' : ('{{ a|timesince:b }}', {'a':now - timedelta(days=2, minutes=1), 'b':now - timedelta(days=2)}, '1\xa0minute'),
47 48
 
48 49
         # Check that timezone is respected
49  
-        'filter-timesince06' : ('{{ a|timesince:b }}', {'a':now_tz - timedelta(hours=8), 'b':now_tz}, '8 hours'),
  50
+        'filter-timesince06' : ('{{ a|timesince:b }}', {'a':now_tz - timedelta(hours=8), 'b':now_tz}, '8\xa0hours'),
50 51
 
51 52
         # Regression for #7443
52  
-        'filter-timesince07': ('{{ earlier|timesince }}', { 'earlier': now - timedelta(days=7) }, '1 week'),
53  
-        'filter-timesince08': ('{{ earlier|timesince:now }}', { 'now': now, 'earlier': now - timedelta(days=7) }, '1 week'),
54  
-        'filter-timesince09': ('{{ later|timesince }}', { 'later': now + timedelta(days=7) }, '0 minutes'),
55  
-        'filter-timesince10': ('{{ later|timesince:now }}', { 'now': now, 'later': now + timedelta(days=7) }, '0 minutes'),
  53
+        'filter-timesince07': ('{{ earlier|timesince }}', { 'earlier': now - timedelta(days=7) }, '1\xa0week'),
  54
+        'filter-timesince08': ('{{ earlier|timesince:now }}', { 'now': now, 'earlier': now - timedelta(days=7) }, '1\xa0week'),
  55
+        'filter-timesince09': ('{{ later|timesince }}', { 'later': now + timedelta(days=7) }, '0\xa0minutes'),
  56
+        'filter-timesince10': ('{{ later|timesince:now }}', { 'now': now, 'later': now + timedelta(days=7) }, '0\xa0minutes'),
56 57
 
57 58
         # Ensures that differing timezones are calculated correctly
58  
-        'filter-timesince11' : ('{{ a|timesince }}', {'a': now}, '0 minutes'),
59  
-        'filter-timesince12' : ('{{ a|timesince }}', {'a': now_tz}, '0 minutes'),
60  
-        'filter-timesince13' : ('{{ a|timesince }}', {'a': now_tz_i}, '0 minutes'),
61  
-        'filter-timesince14' : ('{{ a|timesince:b }}', {'a': now_tz, 'b': now_tz_i}, '0 minutes'),
  59
+        'filter-timesince11' : ('{{ a|timesince }}', {'a': now}, '0\xa0minutes'),
  60
+        'filter-timesince12' : ('{{ a|timesince }}', {'a': now_tz}, '0\xa0minutes'),
  61
+        'filter-timesince13' : ('{{ a|timesince }}', {'a': now_tz_i}, '0\xa0minutes'),
  62
+        'filter-timesince14' : ('{{ a|timesince:b }}', {'a': now_tz, 'b': now_tz_i}, '0\xa0minutes'),
62 63
         'filter-timesince15' : ('{{ a|timesince:b }}', {'a': now, 'b': now_tz_i}, ''),
63 64
         'filter-timesince16' : ('{{ a|timesince:b }}', {'a': now_tz_i, 'b': now}, ''),
64 65
 
65 66
         # Regression for #9065 (two date objects).
66  
-        'filter-timesince17' : ('{{ a|timesince:b }}', {'a': today, 'b': today}, '0 minutes'),
67  
-        'filter-timesince18' : ('{{ a|timesince:b }}', {'a': today, 'b': today + timedelta(hours=24)}, '1 day'),
  67
+        'filter-timesince17' : ('{{ a|timesince:b }}', {'a': today, 'b': today}, '0\xa0minutes'),
  68
+        'filter-timesince18' : ('{{ a|timesince:b }}', {'a': today, 'b': today + timedelta(hours=24)}, '1\xa0day'),
68 69
 
69 70
         # Default compare with datetime.now()
70  
-        'filter-timeuntil01' : ('{{ a|timeuntil }}', {'a':datetime.now() + timedelta(minutes=2, seconds = 10)}, '2 minutes'),
71  
-        'filter-timeuntil02' : ('{{ a|timeuntil }}', {'a':(datetime.now() + timedelta(days=1, seconds = 10))}, '1 day'),
72  
-        'filter-timeuntil03' : ('{{ a|timeuntil }}', {'a':(datetime.now() + timedelta(hours=8, minutes=10, seconds = 10))}, '8 hours, 10 minutes'),
  71
+        'filter-timeuntil01' : ('{{ a|timeuntil }}', {'a':datetime.now() + timedelta(minutes=2, seconds = 10)}, '2\xa0minutes'),
  72
+        'filter-timeuntil02' : ('{{ a|timeuntil }}', {'a':(datetime.now() + timedelta(days=1, seconds = 10))}, '1\xa0day'),
  73
+        'filter-timeuntil03' : ('{{ a|timeuntil }}', {'a':(datetime.now() + timedelta(hours=8, minutes=10, seconds = 10))}, '8\xa0hours, 10\xa0minutes'),
73 74
 
74 75
         # Compare to a given parameter
75  
-        'filter-timeuntil04' : ('{{ a|timeuntil:b }}', {'a':now - timedelta(days=1), 'b':now - timedelta(days=2)}, '1 day'),
76  
-        'filter-timeuntil05' : ('{{ a|timeuntil:b }}', {'a':now - timedelta(days=2), 'b':now - timedelta(days=2, minutes=1)}, '1 minute'),
  76
+        'filter-timeuntil04' : ('{{ a|timeuntil:b }}', {'a':now - timedelta(days=1), 'b':now - timedelta(days=2)}, '1\xa0day'),
  77
+        'filter-timeuntil05' : ('{{ a|timeuntil:b }}', {'a':now - timedelta(days=2), 'b':now - timedelta(days=2, minutes=1)}, '1\xa0minute'),
77 78
 
78 79
         # Regression for #7443
79  
-        'filter-timeuntil06': ('{{ earlier|timeuntil }}', { 'earlier': now - timedelta(days=7) }, '0 minutes'),
80  
-        'filter-timeuntil07': ('{{ earlier|timeuntil:now }}', { 'now': now, 'earlier': now - timedelta(days=7) }, '0 minutes'),
81  
-        'filter-timeuntil08': ('{{ later|timeuntil }}', { 'later': now + timedelta(days=7, hours=1) }, '1 week'),
82  
-        'filter-timeuntil09': ('{{ later|timeuntil:now }}', { 'now': now, 'later': now + timedelta(days=7) }, '1 week'),
  80
+        'filter-timeuntil06': ('{{ earlier|timeuntil }}', { 'earlier': now - timedelta(days=7) }, '0\xa0minutes'),
  81
+        'filter-timeuntil07': ('{{ earlier|timeuntil:now }}', { 'now': now, 'earlier': now - timedelta(days=7) }, '0\xa0minutes'),
  82
+        'filter-timeuntil08': ('{{ later|timeuntil }}', { 'later': now + timedelta(days=7, hours=1) }, '1\xa0week'),
  83
+        'filter-timeuntil09': ('{{ later|timeuntil:now }}', { 'now': now, 'later': now + timedelta(days=7) }, '1\xa0week'),
83 84
 
84 85
         # Ensures that differing timezones are calculated correctly
85  
-        'filter-timeuntil10' : ('{{ a|timeuntil }}', {'a': now_tz_i}, '0 minutes'),
86  
-        'filter-timeuntil11' : ('{{ a|timeuntil:b }}', {'a': now_tz_i, 'b': now_tz}, '0 minutes'),
  86
+        'filter-timeuntil10' : ('{{ a|timeuntil }}', {'a': now_tz_i}, '0\xa0minutes'),
  87
+        'filter-timeuntil11' : ('{{ a|timeuntil:b }}', {'a': now_tz_i, 'b': now_tz}, '0\xa0minutes'),
87 88
 
88 89
         # Regression for #9065 (two date objects).
89  
-        'filter-timeuntil12' : ('{{ a|timeuntil:b }}', {'a': today, 'b': today}, '0 minutes'),
90  
-        'filter-timeuntil13' : ('{{ a|timeuntil:b }}', {'a': today, 'b': today - timedelta(hours=24)}, '1 day'),
  90
+        'filter-timeuntil12' : ('{{ a|timeuntil:b }}', {'a': today, 'b': today}, '0\xa0minutes'),
  91
+        'filter-timeuntil13' : ('{{ a|timeuntil:b }}', {'a': today, 'b': today - timedelta(hours=24)}, '1\xa0day'),
91 92
 
92 93
         'filter-addslash01': ("{% autoescape off %}{{ a|addslashes }} {{ b|addslashes }}{% endautoescape %}", {"a": "<a>'", "b": mark_safe("<a>'")}, r"<a>\' <a>\'"),
93 94
         'filter-addslash02': ("{{ a|addslashes }} {{ b|addslashes }}", {"a": "<a>'", "b": mark_safe("<a>'")}, r"&lt;a&gt;\&#39; <a>\'"),
71  tests/utils_tests/test_timesince.py
@@ -21,32 +21,33 @@ def setUp(self):
21 21
 
22 22
     def test_equal_datetimes(self):
23 23
         """ equal datetimes. """
24  
-        self.assertEqual(timesince(self.t, self.t), '0 minutes')
  24
+        # NOTE: \xa0 avoids wrapping between value and unit
  25
+        self.assertEqual(timesince(self.t, self.t), '0\xa0minutes')
25 26
 
26 27
     def test_ignore_microseconds_and_seconds(self):
27 28
         """ Microseconds and seconds are ignored. """
28 29
         self.assertEqual(timesince(self.t, self.t+self.onemicrosecond),
29  
-            '0 minutes')
  30
+            '0\xa0minutes')
30 31
         self.assertEqual(timesince(self.t, self.t+self.onesecond),
31  
-            '0 minutes')
  32
+            '0\xa0minutes')
32 33
 
33 34
     def test_other_units(self):
34 35
         """ Test other units. """
35 36
         self.assertEqual(timesince(self.t, self.t+self.oneminute),
36  
-            '1 minute')
37  
-        self.assertEqual(timesince(self.t, self.t+self.onehour), '1 hour')
38  
-        self.assertEqual(timesince(self.t, self.t+self.oneday), '1 day')
39  
-        self.assertEqual(timesince(self.t, self.t+self.oneweek), '1 week')
  37
+            '1\xa0minute')
  38
+        self.assertEqual(timesince(self.t, self.t+self.onehour), '1\xa0hour')
  39
+        self.assertEqual(timesince(self.t, self.t+self.oneday), '1\xa0day')
  40
+        self.assertEqual(timesince(self.t, self.t+self.oneweek), '1\xa0week')
40 41
         self.assertEqual(timesince(self.t, self.t+self.onemonth),
41  
-            '1 month')
42  
-        self.assertEqual(timesince(self.t, self.t+self.oneyear), '1 year')
  42
+            '1\xa0month')
  43
+        self.assertEqual(timesince(self.t, self.t+self.oneyear), '1\xa0year')
43 44
 
44 45
     def test_multiple_units(self):
45 46
         """ Test multiple units. """
46 47
         self.assertEqual(timesince(self.t,
47  
-            self.t+2*self.oneday+6*self.onehour), '2 days, 6 hours')
  48
+            self.t+2*self.oneday+6*self.onehour), '2\xa0days, 6\xa0hours')
48 49
         self.assertEqual(timesince(self.t,
49  
-            self.t+2*self.oneweek+2*self.oneday), '2 weeks, 2 days')
  50
+            self.t+2*self.oneweek+2*self.oneday), '2\xa0weeks, 2\xa0days')
50 51
 
51 52
     def test_display_first_unit(self):
52 53
         """
@@ -55,10 +56,10 @@ def test_display_first_unit(self):
55 56
         """
56 57
         self.assertEqual(timesince(self.t,
57 58
             self.t+2*self.oneweek+3*self.onehour+4*self.oneminute),
58  
-            '2 weeks')
  59
+            '2\xa0weeks')
59 60
 
60 61
         self.assertEqual(timesince(self.t,
61  
-            self.t+4*self.oneday+5*self.oneminute), '4 days')
  62
+            self.t+4*self.oneday+5*self.oneminute), '4\xa0days')
62 63
 
63 64
     def test_display_second_before_first(self):
64 65
         """
@@ -66,30 +67,30 @@ def test_display_second_before_first(self):
66 67
         get 0 minutes.
67 68
         """
68 69
         self.assertEqual(timesince(self.t, self.t-self.onemicrosecond),
69  
-            '0 minutes')
  70
+            '0\xa0minutes')
70 71
         self.assertEqual(timesince(self.t, self.t-self.onesecond),
71  
-            '0 minutes')
  72
+            '0\xa0minutes')
72 73
         self.assertEqual(timesince(self.t, self.t-self.oneminute),
73  
-            '0 minutes')
  74
+            '0\xa0minutes')
74 75
         self.assertEqual(timesince(self.t, self.t-self.onehour),
75  
-            '0 minutes')
  76
+            '0\xa0minutes')
76 77
         self.assertEqual(timesince(self.t, self.t-self.oneday),
77  
-            '0 minutes')
  78
+            '0\xa0minutes')
78 79
         self.assertEqual(timesince(self.t, self.t-self.oneweek),
79  
-            '0 minutes')
  80
+            '0\xa0minutes')
80 81
         self.assertEqual(timesince(self.t, self.t-self.onemonth),
81  
-            '0 minutes')
  82
+            '0\xa0minutes')
82 83
         self.assertEqual(timesince(self.t, self.t-self.oneyear),
83  
-            '0 minutes')
  84
+            '0\xa0minutes')
84 85
         self.assertEqual(timesince(self.t,
85  
-            self.t-2*self.oneday-6*self.onehour), '0 minutes')
  86
+            self.t-2*self.oneday-6*self.onehour), '0\xa0minutes')
86 87
         self.assertEqual(timesince(self.t,
87  
-            self.t-2*self.oneweek-2*self.oneday), '0 minutes')
  88
+            self.t-2*self.oneweek-2*self.oneday), '0\xa0minutes')
88 89
         self.assertEqual(timesince(self.t,
89 90
             self.t-2*self.oneweek-3*self.onehour-4*self.oneminute),
90  
-            '0 minutes')
  91
+            '0\xa0minutes')
91 92
         self.assertEqual(timesince(self.t,
92  
-            self.t-4*self.oneday-5*self.oneminute), '0 minutes')
  93
+            self.t-4*self.oneday-5*self.oneminute), '0\xa0minutes')
93 94
 
94 95
     def test_different_timezones(self):
95 96
         """ When using two different timezones. """
@@ -97,28 +98,28 @@ def test_different_timezones(self):
97 98
         now_tz = datetime.datetime.now(LocalTimezone(now))
98 99
         now_tz_i = datetime.datetime.now(FixedOffset((3 * 60) + 15))
99 100
 
100  
-        self.assertEqual(timesince(now), '0 minutes')
101  
-        self.assertEqual(timesince(now_tz), '0 minutes')
102  
-        self.assertEqual(timeuntil(now_tz, now_tz_i), '0 minutes')
  101
+        self.assertEqual(timesince(now), '0\xa0minutes')
  102
+        self.assertEqual(timesince(now_tz), '0\xa0minutes')
  103
+        self.assertEqual(timeuntil(now_tz, now_tz_i), '0\xa0minutes')
103 104
 
104 105
     def test_date_objects(self):
105 106
         """ Both timesince and timeuntil should work on date objects (#17937). """
106 107
         today = datetime.date.today()
107  
-        self.assertEqual(timesince(today + self.oneday), '0 minutes')
108  
-        self.assertEqual(timeuntil(today - self.oneday), '0 minutes')
  108
+        self.assertEqual(timesince(today + self.oneday), '0\xa0minutes')
  109
+        self.assertEqual(timeuntil(today - self.oneday), '0\xa0minutes')
109 110
 
110 111
     def test_both_date_objects(self):
111 112
         """ Timesince should work with both date objects (#9672) """
112 113
         today = datetime.date.today()
113  
-        self.assertEqual(timeuntil(today + self.oneday, today), '1 day')
114  
-        self.assertEqual(timeuntil(today - self.oneday, today), '0 minutes')
115  
-        self.assertEqual(timeuntil(today + self.oneweek, today), '1 week')
  114
+        self.assertEqual(timeuntil(today + self.oneday, today), '1\xa0day')
  115
+        self.assertEqual(timeuntil(today - self.oneday, today), '0\xa0minutes')
  116
+        self.assertEqual(timeuntil(today + self.oneweek, today), '1\xa0week')
116 117
 
117 118
     def test_naive_datetime_with_tzinfo_attribute(self):
118 119
         class naive(datetime.tzinfo):
119 120
             def utcoffset(self, dt):
120 121
                 return None
121 122
         future = datetime.datetime(2080, 1, 1, tzinfo=naive())
122  
-        self.assertEqual(timesince(future), '0 minutes')
  123
+        self.assertEqual(timesince(future), '0\xa0minutes')
123 124
         past = datetime.datetime(1980, 1, 1, tzinfo=naive())
124  
-        self.assertEqual(timeuntil(past), '0 minutes')
  125
+        self.assertEqual(timeuntil(past), '0\xa0minutes')

0 notes on commit 7d77e97

Please sign in to comment.
Something went wrong with that request. Please try again.