def __delitem__(self, id):
"""Delete the message with the specified ID."""
- key = self._key_for(id)
- if key in self._messages:
- del self._messages[key]
+ self.delete(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
+ :return: the message with the specified ID, or `None` if no such
+ message is in the catalog
:rtype: `Message`
"""
- return self._messages.get(self._key_for(id))
+ return self.get(id)
def __setitem__(self, id, message):
"""Add or update the message with the specified ID.
:param message: the `Message` object
"""
assert isinstance(message, Message), 'expected a Message object'
- key = self._key_for(id)
+ key = self._key_for(id, message.context)
current = self._messages.get(key)
if current:
if message.pluralizable and not current.pluralizable:
if errors:
yield message, errors
+ def get(self, id, context=None):
+ """Return the message with the specified ID and context.
+
+ :param id: the message ID
+ :param context: the message context, or ``None`` for no context
+ :return: the message with the specified ID, or `None` if no such
+ message is in the catalog
+ :rtype: `Message`
+ """
+ return self._messages.get(self._key_for(id, context))
+
+ def delete(self, id, context=None):
+ """Delete the message with the specified ID and context.
+
+ :param id: the message ID
+ :param context: the message context, or ``None`` for no context
+ """
+ key = self._key_for(id, context)
+ if key in self._messages:
+ del self._messages[key]
+
def update(self, template, no_fuzzy_matching=False):
"""Update the catalog based on the given template catalog.
# Prepare for fuzzy matching
fuzzy_candidates = []
if not no_fuzzy_matching:
- fuzzy_candidates = [
- self._key_for(msgid) for msgid in messages
- if msgid and messages[msgid].string
- ]
+ fuzzy_candidates = dict([
+ (self._key_for(msgid), messages[msgid].context)
+ for msgid in messages if msgid and messages[msgid].string
+ ])
fuzzy_matches = set()
def _merge(message, oldkey, newkey):
for message in template:
if message.id:
- key = self._key_for(message.id)
+ key = self._key_for(message.id, message.context)
if key in messages:
_merge(message, key, key)
else:
if no_fuzzy_matching is False:
# do some fuzzy matching with difflib
- matches = get_close_matches(key.lower().strip(),
- fuzzy_candidates, 1)
+ if isinstance(key, tuple):
+ matchkey = key[0] # just the msgid, no context
+ else:
+ matchkey = key
+ matches = get_close_matches(matchkey.lower().strip(),
+ fuzzy_candidates.keys(), 1)
if matches:
- _merge(message, matches[0], key)
+ newkey = matches[0]
+ newctxt = fuzzy_candidates[newkey]
+ if newctxt is not None:
+ newkey = newkey, newctxt
+ _merge(message, newkey, key)
continue
self[message.id] = message
if no_fuzzy_matching or msgid not in fuzzy_matches:
self.obsolete[msgid] = remaining[msgid]
- def _key_for(self, id):
+ def _key_for(self, id, context=None):
"""The key for a message is just the singular ID even for pluralizable
+ messages, but is a ``(msgid, msgctxt)`` tuple for context-specific
messages.
"""
key = id
if isinstance(key, (list, tuple)):
key = id[0]
+ if context is not None:
+ key = (key, context)
return key
self.assertEqual(None, cat['foo'].string)
self.assertEqual(False, cat['foo'].fuzzy)
+ def test_update_fuzzy_matching_with_new_context(self):
+ cat = catalog.Catalog()
+ cat.add('foo', 'Voh')
+ cat.add('bar', 'Bahr')
+ tmpl = catalog.Catalog()
+ tmpl.add('Foo', context='Menu')
+ cat.update(tmpl)
+ self.assertEqual(1, len(cat.obsolete))
+ assert 'foo' not in cat
+
+ message = cat.get('Foo', 'Menu')
+ self.assertEqual('Voh', message.string)
+ self.assertEqual(True, message.fuzzy)
+ self.assertEqual('Menu', message.context)
+
+ def test_update_fuzzy_matching_with_changed_context(self):
+ cat = catalog.Catalog()
+ cat.add('foo', 'Voh', context='Menu|File')
+ cat.add('bar', 'Bahr', context='Menu|File')
+ tmpl = catalog.Catalog()
+ tmpl.add('Foo', context='Menu|Edit')
+ cat.update(tmpl)
+ self.assertEqual(1, len(cat.obsolete))
+ assert cat.get('Foo', 'Menu|File') is None
+
+ message = cat.get('Foo', 'Menu|Edit')
+ self.assertEqual('Voh', message.string)
+ self.assertEqual(True, message.fuzzy)
+ self.assertEqual('Menu|Edit', message.context)
+
def test_update_fuzzy_matching_no_cascading(self):
cat = catalog.Catalog()
cat.add('fo', 'Voh')
self.assertEqual(None, cat2['foo'].string)
self.assertEqual(False, cat2['foo'].fuzzy)
+
def suite():
suite = unittest.TestSuite()
suite.addTest(doctest.DocTestSuite(catalog, optionflags=doctest.ELLIPSIS))