changeset 392:34c0a25b1ed7

Preliminary support for timedelta formatting (see #126), and import/expose the locale plural rules from the CLDR.
author cmlenz
date Mon, 14 Jul 2008 22:13:44 +0000
parents d21f92883d82
children dd0df1242ae3
files babel/core.py babel/dates.py babel/plural.py scripts/import_cldr.py
diffstat 4 files changed, 87 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/babel/core.py
+++ b/babel/core.py
@@ -601,6 +601,21 @@
         :type: `dict`
         """)
 
+    def plural_form(self):
+        return self._data['plural_form']
+    plural_form = property(plural_form, doc="""\
+        Plural rules for the locale.
+        
+        >>> Locale('en').plural_form(1)
+        'one'
+        >>> Locale('en').plural_form(0)
+        'other'
+        >>> Locale('fr').plural_form(0)
+        'one'
+        
+        :type: `PluralRule`
+        """)
+
 
 def default_locale(category=None):
     """Returns the system default locale for a given category, based on
--- a/babel/dates.py
+++ b/babel/dates.py
@@ -578,7 +578,7 @@
     if isinstance(time, datetime):
         if tzinfo is not None:
             time = time.astimezone(tzinfo)
-            if hasattr(tzinfo, 'localize'): # pytz
+            if hasattr(tzinfo, 'normalize'): # pytz
                 time = tzinfo.normalize(time)
         time = time.timetz()
     elif tzinfo is not None:
@@ -589,6 +589,42 @@
         format = get_time_format(format, locale=locale)
     return parse_pattern(format).apply(time, locale)
 
+TIMEDELTA_UNITS = (
+    ('year',   3600 * 24 * 365),
+    ('month',  3600 * 24 * 30),
+    ('week',   3600 * 24 * 7),
+    ('day',    3600 * 24),
+    ('hour',   3600),
+    ('minute', 60),
+    ('second', 1)
+)
+
+def format_timedelta(delta, granularity='second', threshold=.9, locale=LC_TIME):
+    """Return a time delta according to the rules of the given locale.
+    
+    >>> format_timedelta(timedelta(weeks=12), locale='en_US')
+    u'3 months'
+    >>> format_timedelta(timedelta(seconds=1), locale='es')
+    u'1 segundo'
+    >>> format_timedelta(timedelta(seconds=1), locale='en_US')
+    u'1 second'
+    
+    :param delta: a ``timedelta`` object representing the time difference to
+                  format
+    
+    """
+    locale = Locale.parse(locale)
+    seconds = int((delta.days * 86400) + delta.seconds)
+
+    for unit, limit in TIMEDELTA_UNITS:
+        r = float(abs(seconds)) / float(limit)
+        if r >= threshold or unit == granularity:
+            r = int(round(r))
+            plural_form = locale.plural_form(r)
+            pattern = locale._data['unit_patterns'][unit][plural_form]
+            return pattern.replace('{0}', str(r))
+    return ''
+
 def parse_date(string, locale=LC_TIME):
     """Parse a date from a string.
     
--- a/babel/plural.py
+++ b/babel/plural.py
@@ -64,6 +64,9 @@
             found.add(key)
             self.abstract.append((key, _Parser(expr).ast))
 
+    def __repr__(self):
+        return '<%s %r>' % (type(self).__name__, self.abstract)
+
     def parse(cls, rules):
         """Create a `PluralRule` instance for the given rules.  If the rules
         are a `PluralRule` object, that object is returned.
@@ -272,6 +275,7 @@
     ]
 
     def __init__(self, string):
+        string = string.lower()
         result = []
         pos = 0
         end = len(string)
@@ -320,7 +324,7 @@
     def and_condition(self):
         op = self.relation()
         while self.skip('word', 'and'):
-            op = 'and', (op, self.realation())
+            op = 'and', (op, self.relation())
         return op
 
     def relation(self):
--- a/scripts/import_cldr.py
+++ b/scripts/import_cldr.py
@@ -27,6 +27,7 @@
 sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), '..'))
 
 from babel import dates, numbers
+from babel.plural import PluralRule
 from babel.localedata import Alias
 
 weekdays = {'mon': 0, 'tue': 1, 'wed': 2, 'thu': 3, 'fri': 4, 'sat': 5,
@@ -131,6 +132,17 @@
                 containers |= territory_containment[group]
             containers.add(group)
 
+    # prepare the per-locale plural rules definitions
+    plural_rules = {}
+    prsup = parse(os.path.join(srcdir, 'supplemental', 'plurals.xml'))
+    for elem in prsup.findall('//plurals/pluralRules'):
+        rules = []
+        for rule in elem.findall('pluralRule'):
+            rules.append((rule.attrib['count'], unicode(rule.text)))
+        pr = PluralRule(rules)
+        for locale in elem.attrib['locales'].split():
+            plural_rules[locale] = pr
+
     filenames = os.listdir(os.path.join(srcdir, 'main'))
     filenames.remove('root.xml')
     filenames.sort(lambda a,b: len(a)-len(b))
@@ -161,6 +173,14 @@
         regions = territory_containment.get(territory, [])
         print>>sys.stderr, '  Regions:    %r' % regions
 
+        # plural rules
+        locale_id = '_'.join(filter(None, [
+            language,
+            territory != '001' and territory or None
+        ]))
+        if locale_id in plural_rules:
+            data['plural_form'] = plural_rules[locale_id]
+
         # <localeDisplayNames>
 
         territories = data.setdefault('territories', {})
@@ -453,6 +473,16 @@
                     and 'choice' not in symbol.attrib:
                 currency_symbols[code] = unicode(symbol.text)
 
+        # <units>
+
+        unit_patterns = data.setdefault('unit_patterns', {})
+        for elem in tree.findall('//units/unit'):
+            unit_type = elem.attrib['type']
+            unit_pattern = unit_patterns.setdefault(unit_type, {})
+            for pattern in elem.findall('unitPattern'):
+                unit_patterns[unit_type][pattern.attrib['count']] = \
+                        unicode(pattern.text)
+
         outfile = open(os.path.join(destdir, 'localedata', stem + '.dat'), 'wb')
         try:
             pickle.dump(data, outfile, 2)
Copyright (C) 2012-2017 Edgewall Software