diff babel/messages/catalog.py @ 222:bd8b1301b27e

Added infrastructure for adding catalog checkers, and implement a checker that validations Python format parameters in translations, closing #19.
author cmlenz
date Mon, 16 Jul 2007 16:57:49 +0000
parents 2c00a52bc073
children f358dd40a960
line wrap: on
line diff
--- a/babel/messages/catalog.py
+++ b/babel/messages/catalog.py
@@ -30,17 +30,17 @@
 from babel.messages.plurals import PLURALS
 from babel.util import odict, LOCALTZ, UTC, FixedOffsetTimezone
 
-__all__ = ['Message', 'Catalog']
+__all__ = ['Message', 'Catalog', 'TranslationError']
 __docformat__ = 'restructuredtext en'
 
-PYTHON_FORMAT = re.compile(r'\%(\([\w]+\))?[diouxXeEfFgGcrs]').search
+PYTHON_FORMAT = re.compile(r'\%(\([\w]+\))?[diouxXeEfFgGcrs]')
 
 
 class Message(object):
     """Representation of a single message in a catalog."""
 
     def __init__(self, id, string=u'', locations=(), flags=(), auto_comments=(),
-                 user_comments=(), previous_id=()):
+                 user_comments=(), previous_id=(), lineno=None):
         """Create the message object.
 
         :param id: the message ID, or a ``(singular, plural)`` tuple for
@@ -53,6 +53,8 @@
         :param user_comments: a sequence of user comments for the message
         :param previous_id: the previous message ID, or a ``(singular, plural)``
                             tuple for pluralizable messages
+        :param lineno: the line number on which the msgid line was found in the
+                       PO file, if any
         """
         self.id = id #: The message ID
         if not string and self.pluralizable:
@@ -70,6 +72,7 @@
             self.previous_id = [previous_id]
         else:
             self.previous_id = list(previous_id)
+        self.lineno = lineno
 
     def __repr__(self):
         return '<%s %r (flags: %r)>' % (type(self).__name__, self.id,
@@ -108,7 +111,7 @@
         ids = self.id
         if not isinstance(ids, (list, tuple)):
             ids = [ids]
-        return bool(filter(None, [PYTHON_FORMAT(id) for id in ids]))
+        return bool(filter(None, [PYTHON_FORMAT.search(id) for id in ids]))
     python_format = property(python_format, doc="""\
         Whether the message contains Python-style parameters.
 
@@ -121,6 +124,11 @@
         """)
 
 
+class TranslationError(Exception):
+    """Exception thrown by translation checkers when invalid message
+    translations are encountered."""
+
+
 DEFAULT_HEADER = u"""\
 # Translations template for PROJECT.
 # Copyright (C) YEAR ORGANIZATION
@@ -480,7 +488,7 @@
             self._messages[key] = message
 
     def add(self, id, string=None, locations=(), flags=(), auto_comments=(),
-            user_comments=(), previous_id=()):
+            user_comments=(), previous_id=(), lineno=None):
         """Add or update the message with the specified ID.
 
         >>> catalog = Catalog()
@@ -501,9 +509,35 @@
         :param user_comments: a sequence of user comments
         :param previous_id: the previous message ID, or a ``(singular, plural)``
                             tuple for pluralizable messages
+        :param lineno: the line number on which the msgid line was found in the
+                       PO file, if any
         """
         self[id] = Message(id, string, list(locations), flags, auto_comments,
-                           user_comments, previous_id)
+                           user_comments, previous_id, lineno=lineno)
+
+    def check(self):
+        """Run various validation checks on the translations in the catalog.
+        
+        For every message which fails validation, this method yield a
+        ``(message, errors)`` tuple, where ``message`` is the `Message` object
+        and ``errors`` is a sequence of `TranslationError` objects.
+        
+        :rtype: ``iterator``
+        """
+        checkers = []
+        from pkg_resources import working_set
+        for entry_point in working_set.iter_entry_points('babel.checkers'):
+            checkers.append(entry_point.load())
+
+        for message in self._messages.values():
+            errors = []
+            for checker in checkers:
+                try:
+                    checker(self, message)
+                except TranslationError, e:
+                    errors.append(e)
+            if errors:
+                yield message, errors
 
     def update(self, template, no_fuzzy_matching=False):
         """Update the catalog based on the given template catalog.
Copyright (C) 2012-2017 Edgewall Software