# HG changeset patch # User cmlenz # Date 1181308532 0 # Node ID ef318245cfe52d69b4656c1b4eb5fba1208e018b # Parent 47353672f1a8e5c8e3bc5db26ef9ed9e9e55d828 `read_po` now returns a `Catalog`. diff --git a/babel/messages/catalog.py b/babel/messages/catalog.py --- 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'] + + + 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'] + + + 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) diff --git a/babel/messages/pofile.py b/babel/messages/pofile.py --- 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.