changeset 66:d1a7425739d3

`read_po` now returns a `Catalog`.
author cmlenz
date Fri, 08 Jun 2007 13:15:32 +0000
parents b7b61e0fd23e
children ad48b95af0d9
files babel/messages/catalog.py babel/messages/pofile.py
diffstat 2 files changed, 94 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/babel/messages/catalog.py
+++ b/babel/messages/catalog.py
@@ -19,6 +19,7 @@
 except NameError:
     from sets import Set as set
 
+from babel.core import Locale
 from babel.util import odict
 
 __all__ = ['Message', 'Catalog']
@@ -85,25 +86,72 @@
 class Catalog(object):
     """Representation a message catalog."""
 
-    def __init__(self, domain=None):
-        self.domain = domain
-        self.messages = odict()
+    def __init__(self, domain=None, locale=None):
+        """Initialize the catalog object.
+        
+        :param domain: the message domain
+        :param locale: the locale identifier or `Locale` object, or `None`
+                       if the catalog is not bound to a locale (which basically
+                       means it's a template)
+        """
+        self.domain = domain #: the message domain
+        if locale:
+            locale = Locale.parse(locale)
+        self.locale = locale #: the locale or `None`
+        self.messages = odict() #: the actual `Message` entries by ID
 
     def __iter__(self):
+        """Iterates through all the entries in the catalog, in the order they
+        were added, yielding a `Message` object for every entry.
+        
+        :rtype: ``iterator``
+        """
         for id in self.messages:
             yield self.messages[id]
 
     def __repr__(self):
-        return '<%s %r>' % (type(self).__name__, self.domain)
+        locale = ''
+        if self.locale:
+            locale = ' %s' % self.locale
+        return '<%s %r%s>' % (type(self).__name__, self.domain, locale)
 
     def __delitem__(self, id):
+        """Delete the message with the specified ID."""
         if id in self.messaages:
             del self.messages[id]
 
     def __getitem__(self, id):
+        """Return the message with the specified ID.
+        
+        :param id: the message ID
+        :return: the message with the specified ID, or `None` if no such message
+                 is in the catalog
+        :rytpe: `Message`
+        """
         return self.messages.get(id)
 
     def __setitem__(self, id, message):
+        """Add or update the message with the specified ID.
+        
+        >>> catalog = Catalog()
+        >>> catalog[u'foo'] = Message(u'foo')
+        >>> catalog[u'foo']
+        <Message u'foo'>
+        
+        If a message with that ID is already in the catalog, it is updated
+        to include the locations and flags of the new message.
+        
+        >>> catalog = Catalog()
+        >>> catalog[u'foo'] = Message(u'foo', locations=[('main.py', 1)])
+        >>> catalog[u'foo'].locations
+        [('main.py', 1)]
+        >>> catalog[u'foo'] = Message(u'foo', locations=[('utils.py', 5)])
+        >>> catalog[u'foo'].locations
+        [('main.py', 1), ('utils.py', 5)]
+        
+        :param id: the message ID
+        :param message: the `Message` object
+        """
         assert isinstance(message, Message), 'expected a Message object'
         current = self.messages.get(id)
         if current:
@@ -118,4 +166,21 @@
             self.messages[id] = message
 
     def add(self, id, string=None, locations=(), flags=()):
+        """Add or update the message with the specified ID.
+        
+        >>> catalog = Catalog()
+        >>> catalog.add(u'foo')
+        >>> catalog[u'foo']
+        <Message u'foo'>
+        
+        This method simply constructs a `Message` object with the given
+        arguments and invokes `__setitem__` with that object.
+        
+        :param id: the message ID, or a ``(singular, plural)`` tuple for
+                   pluralizable messages
+        :param string: the translated message string, or a
+                       ``(singular, plural)`` tuple for pluralizable messages
+        :param locations: a sequence of ``(filenname, lineno)`` tuples
+        :param flags: a set or sequence of flags
+        """
         self[id] = Message(id, string, locations, flags)
--- a/babel/messages/pofile.py
+++ b/babel/messages/pofile.py
@@ -34,20 +34,7 @@
 
 def read_po(fileobj):
     """Read messages from a ``gettext`` PO (portable object) file from the given
-    file-like object.
-    
-    This function yields tuples of the form:
-    
-        ``(message, translation, locations, flags)``
-    
-    where:
-    
-     * ``message`` is the original (untranslated) message, or a
-       ``(singular, plural)`` tuple for pluralizable messages
-     * ``translation`` is the translation of the message, or a tuple of
-       translations for pluralizable messages
-     * ``locations`` is a sequence of ``(filename, lineno)`` tuples
-     * ``flags`` is a set of strings (for exampe, "fuzzy")
+    file-like object and return a `Catalog`.
     
     >>> from StringIO import StringIO
     >>> buf = StringIO('''
@@ -62,40 +49,46 @@
     ... msgstr[0] ""
     ... msgstr[1] ""
     ... ''')
-    >>> for message, translation, locations, flags in read_po(buf):
-    ...     print (message, translation)
-    ...     print ' ', (locations, flags)
-    (('foo %(name)s',), ('',))
-      ((('main.py', 1),), set(['fuzzy', 'python-format']))
+    >>> catalog = read_po(buf)
+    >>> for message in catalog:
+    ...     print (message.id, message.string)
+    ...     print ' ', (message.locations, message.flags)
+    ('foo %(name)s', '')
+      ([('main.py', 1)], set(['fuzzy', 'python-format']))
     (('bar', 'baz'), ('', ''))
-      ((('main.py', 3),), set([]))
+      ([('main.py', 3)], set([]))
     
     :param fileobj: the file-like object to read the PO file from
     :return: an iterator over ``(message, translation, location)`` tuples
     :rtype: ``iterator``
     """
+    catalog = Catalog()
+
     messages = []
     translations = []
     locations = []
     flags = []
     in_msgid = in_msgstr = False
 
-    def pack():
+    def _add_message():
         translations.sort()
-        retval = (tuple(messages), tuple([t[1] for t in translations]),
-                  tuple(locations), set(flags))
-        del messages[:]
-        del translations[:]
-        del locations[:]
-        del flags[:]
-        return retval
+        if len(messages) > 1:
+            msgid = tuple(messages)
+        else:
+            msgid = messages[0]
+        if len(translations) > 1:
+            string = tuple([t[1] for t in translations])
+        else:
+            string = translations[0][1]
+        catalog.add(msgid, string, list(locations), set(flags))
+        del messages[:]; del translations[:]; del locations[:]; del flags[:]
 
     for line in fileobj.readlines():
         line = line.strip()
         if line.startswith('#'):
             in_msgid = in_msgstr = False
             if messages:
-                yield pack()
+                _add_message()
             if line[1:].startswith(':'):
                 for location in line[2:].lstrip().split():
                     filename, lineno = location.split(':', 1)
@@ -111,7 +104,7 @@
             elif line.startswith('msgid'):
                 in_msgid = True
                 if messages:
-                    yield pack()
+                    _add_message()
                 msg = line[5:].lstrip()
                 messages.append(msg[1:-1])
             elif line.startswith('msgstr'):
@@ -130,7 +123,8 @@
                     translations[-1][1] += line.rstrip()[1:-1]
 
     if messages:
-        yield pack()
+        _add_message()
+    return catalog
 
 POT_HEADER = """\
 # Translations Template for %%(project)s.
Copyright (C) 2012-2017 Edgewall Software