Skip to content

Commit 57c5533

Browse files
author
Alexander Smishlajev
committed
fix i18n - mainly plural forms in Interval.pretty()
Date and Interval constructors accept translator object to allow locale switching at runtime.
1 parent 09abbbd commit 57c5533

File tree

1 file changed

+74
-58
lines changed

1 file changed

+74
-58
lines changed

roundup/date.py

Lines changed: 74 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@
1414
# FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
17-
#
18-
# $Id: date.py,v 1.68 2004-05-06 02:35:46 richard Exp $
17+
#
18+
# $Id: date.py,v 1.69 2004-05-19 17:12:18 a1s Exp $
1919

2020
"""Date, time and time interval handling.
2121
"""
2222
__docformat__ = 'restructuredtext'
2323

2424
import time, re, calendar, types
2525
from types import *
26-
from i18n import _
26+
import i18n
2727

2828
def _add_granularity(src, order, value = 1):
2929
'''Increment first non-None value in src dictionary ordered by 'order'
@@ -43,7 +43,7 @@ class Date:
4343
"2000-06-24.13:03:59". We'll call this the "full date format". When
4444
Timestamp objects are printed as strings, they appear in the full date
4545
format with the time always given in GMT. The full date format is
46-
always exactly 19 characters long.
46+
always exactly 19 characters long.
4747
4848
For user input, some partial forms are also permitted: the whole time
4949
or just the seconds may be omitted; and the whole date may be omitted
@@ -109,15 +109,21 @@ class Date:
109109
<Date 2003-07-01.00:00:0.000000>
110110
'''
111111

112-
def __init__(self, spec='.', offset=0, add_granularity=0):
112+
def __init__(self, spec='.', offset=0, add_granularity=0, translator=i18n):
113113
"""Construct a date given a specification and a time zone offset.
114114
115115
'spec'
116116
is a full date or a partial form, with an optional added or
117117
subtracted interval. Or a date 9-tuple.
118118
'offset'
119119
is the local time zone offset from GMT in hours.
120+
'translator'
121+
is i18n module or one of gettext translation classes.
122+
It must have attributes 'gettext' and 'ngettext',
123+
serving as translation functions.
120124
"""
125+
self._ = translator.gettext
126+
self.ngettext = translator.ngettext
121127
if type(spec) == type(''):
122128
self.set(spec, offset=offset, add_granularity=add_granularity)
123129
return
@@ -159,7 +165,7 @@ def set(self, spec, offset=0, date_re=re.compile(r'''
159165
# not serialised data, try usual format
160166
m = date_re.match(spec)
161167
if m is None:
162-
raise ValueError, _('Not a date spec: %s' % self.usagespec)
168+
raise ValueError, self._('Not a date spec: %s') % self.usagespec
163169

164170
info = m.groupdict()
165171

@@ -170,7 +176,7 @@ def set(self, spec, offset=0, date_re=re.compile(r'''
170176
ts = time.time()
171177
frac = ts - int(ts)
172178
y,m,d,H,M,S,x,x,x = time.gmtime(ts)
173-
# gmtime loses the fractional seconds
179+
# gmtime loses the fractional seconds
174180
S = S + frac
175181

176182
if info['y'] is not None or info['a'] is not None:
@@ -197,7 +203,7 @@ def set(self, spec, offset=0, date_re=re.compile(r'''
197203

198204
if add_granularity:
199205
S = S - 1
200-
206+
201207
# now handle the adjustment of hour
202208
frac = S - int(S)
203209
ts = calendar.timegm((y,m,d,H,M,S,0,0,0))
@@ -210,7 +216,7 @@ def set(self, spec, offset=0, date_re=re.compile(r'''
210216
try:
211217
self.applyInterval(Interval(info['o'], allowdate=0))
212218
except ValueError:
213-
raise ValueError, _('%r not a date spec (%s)')%(spec,
219+
raise ValueError, self._('%r not a date spec (%s)')%(spec,
214220
self.usagespec)
215221

216222
def addInterval(self, interval):
@@ -249,11 +255,11 @@ def get_mdays(year, month):
249255

250256
while month < 1 or month > 12 or day < 1 or day > get_mdays(year,month):
251257
# now to day under/over
252-
if day < 1:
258+
if day < 1:
253259
# When going backwards, decrement month, then increment days
254260
month -= 1
255261
day += get_mdays(year,month)
256-
elif day > get_mdays(year,month):
262+
elif day > get_mdays(year,month):
257263
# When going forwards, decrement days, then increment month
258264
day -= get_mdays(year,month)
259265
month += 1
@@ -432,8 +438,12 @@ class Interval:
432438
433439
TODO: more examples, showing the order of addition operation
434440
'''
435-
def __init__(self, spec, sign=1, allowdate=1, add_granularity=0):
441+
def __init__(self, spec, sign=1, allowdate=1, add_granularity=0,
442+
translator=i18n
443+
):
436444
"""Construct an interval given a specification."""
445+
self._ = translator.gettext
446+
self.ngettext = translator.ngettext
437447
if type(spec) in (IntType, FloatType, LongType):
438448
self.from_seconds(spec)
439449
elif type(spec) in (StringType, UnicodeType):
@@ -474,8 +484,8 @@ def set(self, spec, allowdate=1, interval_re=re.compile('''
474484
if not m:
475485
m = interval_re.match(spec)
476486
if not m:
477-
raise ValueError, _('Not an interval spec: [+-] [#y] [#m] [#w] '
478-
'[#d] [[[H]H:MM]:SS] [date spec]')
487+
raise ValueError, self._('Not an interval spec:'
488+
' [+-] [#y] [#m] [#w] [#d] [[[H]H:MM]:SS] [date spec]')
479489
else:
480490
allowdate = 0
481491

@@ -493,8 +503,8 @@ def set(self, spec, allowdate=1, interval_re=re.compile('''
493503

494504
# make sure it's valid
495505
if not valid and not info['D']:
496-
raise ValueError, _('Not an interval spec: [+-] [#y] [#m] [#w] '
497-
'[#d] [[[H]H:MM]:SS]')
506+
raise ValueError, self._('Not an interval spec:'
507+
' [+-] [#y] [#m] [#w] [#d] [[[H]H:MM]:SS]')
498508

499509
if self.week:
500510
self.day = self.day + self.week*7
@@ -625,55 +635,61 @@ def __repr__(self):
625635
def pretty(self):
626636
''' print up the date date using one of these nice formats..
627637
'''
638+
_quarters = self.minute / 15
628639
if self.year:
629-
if self.year == 1:
630-
s = _('1 year')
631-
else:
632-
s = _('%(number)s years')%{'number': self.year}
633-
elif self.month or self.day > 13:
634-
days = (self.month * 30) + self.day
635-
if days > 28:
636-
if int(days/30) > 1:
637-
s = _('%(number)s months')%{'number': int(days/30)}
638-
else:
639-
s = _('1 month')
640-
else:
641-
s = _('%(number)s weeks')%{'number': int(days/7)}
640+
s = self.ngettext("%(number)s year", "%(number)s years",
641+
self.year) % {'number': self.year}
642+
elif self.month or self.day > 28:
643+
_months = int(((self.month * 30) + self.day) / 30)
644+
s = self.ngettext("%(number)s month", "%(number)s months",
645+
_months) % {'number': _months}
642646
elif self.day > 7:
643-
s = _('1 week')
647+
_weeks = int(self.day / 7)
648+
s = self.ngettext("%(number)s week", "%(number)s weeks",
649+
_weeks) % {'number': _weeks}
644650
elif self.day > 1:
645-
s = _('%(number)s days')%{'number': self.day}
651+
# Note: singular form is not used
652+
s = self.ngettext('%(number)s day', '%(number)s days',
653+
self.day) % {'number': self.day}
646654
elif self.day == 1 or self.hour > 12:
647655
if self.sign > 0:
648-
return _('tomorrow')
656+
return self._('tomorrow')
649657
else:
650-
return _('yesterday')
658+
return self._('yesterday')
651659
elif self.hour > 1:
652-
s = _('%(number)s hours')%{'number': self.hour}
660+
# Note: singular form is not used
661+
s = self.ngettext('%(number)s hour', '%(number)s hours',
662+
self.hour) % {'number': self.hour}
653663
elif self.hour == 1:
654664
if self.minute < 15:
655-
s = _('an hour')
656-
elif self.minute/15 == 2:
657-
s = _('1 1/2 hours')
665+
s = self._('an hour')
666+
elif _quarters == 2:
667+
s = self._('1 1/2 hours')
658668
else:
659-
s = _('1 %(number)s/4 hours')%{'number': self.minute/15}
669+
s = self.ngettext('1 %(number)s/4 hours',
670+
'1 %(number)s/4 hours', _quarters) % {'number': _quarters}
660671
elif self.minute < 1:
661672
if self.sign > 0:
662-
return _('in a moment')
673+
return self._('in a moment')
663674
else:
664-
return _('just now')
675+
return self._('just now')
665676
elif self.minute == 1:
666-
s = _('1 minute')
677+
# Note: used in expressions "in 1 minute" or "1 minute ago"
678+
s = self._('1 minute')
667679
elif self.minute < 15:
668-
s = _('%(number)s minutes')%{'number': self.minute}
669-
elif int(self.minute/15) == 2:
670-
s = _('1/2 an hour')
680+
# Note: used in expressions "in 2 minutes" or "2 minutes ago"
681+
s = self.ngettext('%(number)s minute', '%(number)s minutes',
682+
self.minute) % {'number': self.minute}
683+
elif _quarters == 2:
684+
s = self._('1/2 an hour')
671685
else:
672-
s = _('%(number)s/4 hour')%{'number': int(self.minute/15)}
673-
if self.sign < 0:
674-
s = s + _(' ago')
686+
s = self.ngettext('%(number)s/4 hours', '%(number)s/4 hours',
687+
_quarters) % {'number': _quarters}
688+
# XXX this is internationally broken
689+
if self.sign < 0:
690+
s = self._('%s ago') % s
675691
else:
676-
s = _('in ') + s
692+
s = self._('in %s') % s
677693
return s
678694

679695
def get_tuple(self):
@@ -687,7 +703,7 @@ def serialise(self):
687703

688704
def as_seconds(self):
689705
'''Calculate the Interval as a number of seconds.
690-
706+
691707
Months are counted as 30 days, years as 365 days. Returns a Long
692708
int.
693709
'''
@@ -756,7 +772,7 @@ def fixTimeOverflow(time):
756772
class Range:
757773
"""Represents range between two values
758774
Ranges can be created using one of theese two alternative syntaxes:
759-
775+
760776
1. Native english syntax::
761777
762778
[[From] <value>][ To <value>]
@@ -774,33 +790,33 @@ class Range:
774790
775791
>>> Range("from 2-12 to 4-2")
776792
<Range from 2003-02-12.00:00:00 to 2003-04-02.00:00:00>
777-
793+
778794
>>> Range("18:00 TO +2m")
779795
<Range from 2003-03-08.18:00:00 to 2003-05-08.20:07:48>
780-
796+
781797
>>> Range("12:00")
782798
<Range from 2003-03-08.12:00:00 to None>
783-
799+
784800
>>> Range("tO +3d")
785801
<Range from None to 2003-03-11.20:07:48>
786-
802+
787803
>>> Range("2002-11-10; 2002-12-12")
788804
<Range from 2002-11-10.00:00:00 to 2002-12-12.00:00:00>
789-
805+
790806
>>> Range("; 20:00 +1d")
791807
<Range from None to 2003-03-09.20:00:00>
792808
793809
"""
794810
def __init__(self, spec, Type, allow_granularity=1, **params):
795811
"""Initializes Range of type <Type> from given <spec> string.
796-
812+
797813
Sets two properties - from_value and to_value. None assigned to any of
798814
this properties means "infinitum" (-infinitum to from_value and
799815
+infinitum to to_value)
800816
801817
The Type parameter here should be class itself (e.g. Date), not a
802818
class instance.
803-
819+
804820
"""
805821
self.range_type = Type
806822
re_range = r'(?:^|from(.+?))(?:to(.+?)$|$)'
@@ -830,7 +846,7 @@ def __str__(self):
830846

831847
def __repr__(self):
832848
return "<Range %s>" % self.__str__()
833-
849+
834850
def test_range():
835851
rspecs = ("from 2-12 to 4-2", "from 18:00 TO +2m", "12:00;", "tO +3d",
836852
"2002-11-10; 2002-12-12", "; 20:00 +1d", '2002-10-12')

0 commit comments

Comments
 (0)