Mercurial > babel > old > mirror
comparison 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 |
comparison
equal
deleted
inserted
replaced
221:19eaa0f8fae5 | 222:bd8b1301b27e |
---|---|
28 from babel.core import Locale | 28 from babel.core import Locale |
29 from babel.dates import format_datetime | 29 from babel.dates import format_datetime |
30 from babel.messages.plurals import PLURALS | 30 from babel.messages.plurals import PLURALS |
31 from babel.util import odict, LOCALTZ, UTC, FixedOffsetTimezone | 31 from babel.util import odict, LOCALTZ, UTC, FixedOffsetTimezone |
32 | 32 |
33 __all__ = ['Message', 'Catalog'] | 33 __all__ = ['Message', 'Catalog', 'TranslationError'] |
34 __docformat__ = 'restructuredtext en' | 34 __docformat__ = 'restructuredtext en' |
35 | 35 |
36 PYTHON_FORMAT = re.compile(r'\%(\([\w]+\))?[diouxXeEfFgGcrs]').search | 36 PYTHON_FORMAT = re.compile(r'\%(\([\w]+\))?[diouxXeEfFgGcrs]') |
37 | 37 |
38 | 38 |
39 class Message(object): | 39 class Message(object): |
40 """Representation of a single message in a catalog.""" | 40 """Representation of a single message in a catalog.""" |
41 | 41 |
42 def __init__(self, id, string=u'', locations=(), flags=(), auto_comments=(), | 42 def __init__(self, id, string=u'', locations=(), flags=(), auto_comments=(), |
43 user_comments=(), previous_id=()): | 43 user_comments=(), previous_id=(), lineno=None): |
44 """Create the message object. | 44 """Create the message object. |
45 | 45 |
46 :param id: the message ID, or a ``(singular, plural)`` tuple for | 46 :param id: the message ID, or a ``(singular, plural)`` tuple for |
47 pluralizable messages | 47 pluralizable messages |
48 :param string: the translated message string, or a | 48 :param string: the translated message string, or a |
51 :param flags: a set or sequence of flags | 51 :param flags: a set or sequence of flags |
52 :param auto_comments: a sequence of automatic comments for the message | 52 :param auto_comments: a sequence of automatic comments for the message |
53 :param user_comments: a sequence of user comments for the message | 53 :param user_comments: a sequence of user comments for the message |
54 :param previous_id: the previous message ID, or a ``(singular, plural)`` | 54 :param previous_id: the previous message ID, or a ``(singular, plural)`` |
55 tuple for pluralizable messages | 55 tuple for pluralizable messages |
56 :param lineno: the line number on which the msgid line was found in the | |
57 PO file, if any | |
56 """ | 58 """ |
57 self.id = id #: The message ID | 59 self.id = id #: The message ID |
58 if not string and self.pluralizable: | 60 if not string and self.pluralizable: |
59 string = (u'', u'') | 61 string = (u'', u'') |
60 self.string = string #: The message translation | 62 self.string = string #: The message translation |
68 self.user_comments = list(user_comments) | 70 self.user_comments = list(user_comments) |
69 if isinstance(previous_id, basestring): | 71 if isinstance(previous_id, basestring): |
70 self.previous_id = [previous_id] | 72 self.previous_id = [previous_id] |
71 else: | 73 else: |
72 self.previous_id = list(previous_id) | 74 self.previous_id = list(previous_id) |
75 self.lineno = lineno | |
73 | 76 |
74 def __repr__(self): | 77 def __repr__(self): |
75 return '<%s %r (flags: %r)>' % (type(self).__name__, self.id, | 78 return '<%s %r (flags: %r)>' % (type(self).__name__, self.id, |
76 list(self.flags)) | 79 list(self.flags)) |
77 | 80 |
106 | 109 |
107 def python_format(self): | 110 def python_format(self): |
108 ids = self.id | 111 ids = self.id |
109 if not isinstance(ids, (list, tuple)): | 112 if not isinstance(ids, (list, tuple)): |
110 ids = [ids] | 113 ids = [ids] |
111 return bool(filter(None, [PYTHON_FORMAT(id) for id in ids])) | 114 return bool(filter(None, [PYTHON_FORMAT.search(id) for id in ids])) |
112 python_format = property(python_format, doc="""\ | 115 python_format = property(python_format, doc="""\ |
113 Whether the message contains Python-style parameters. | 116 Whether the message contains Python-style parameters. |
114 | 117 |
115 >>> Message('foo %(name)s bar').python_format | 118 >>> Message('foo %(name)s bar').python_format |
116 True | 119 True |
117 >>> Message(('foo %(name)s', 'foo %(name)s')).python_format | 120 >>> Message(('foo %(name)s', 'foo %(name)s')).python_format |
118 True | 121 True |
119 | 122 |
120 :type: `bool` | 123 :type: `bool` |
121 """) | 124 """) |
125 | |
126 | |
127 class TranslationError(Exception): | |
128 """Exception thrown by translation checkers when invalid message | |
129 translations are encountered.""" | |
122 | 130 |
123 | 131 |
124 DEFAULT_HEADER = u"""\ | 132 DEFAULT_HEADER = u"""\ |
125 # Translations template for PROJECT. | 133 # Translations template for PROJECT. |
126 # Copyright (C) YEAR ORGANIZATION | 134 # Copyright (C) YEAR ORGANIZATION |
478 if isinstance(id, (list, tuple)): | 486 if isinstance(id, (list, tuple)): |
479 assert isinstance(message.string, (list, tuple)) | 487 assert isinstance(message.string, (list, tuple)) |
480 self._messages[key] = message | 488 self._messages[key] = message |
481 | 489 |
482 def add(self, id, string=None, locations=(), flags=(), auto_comments=(), | 490 def add(self, id, string=None, locations=(), flags=(), auto_comments=(), |
483 user_comments=(), previous_id=()): | 491 user_comments=(), previous_id=(), lineno=None): |
484 """Add or update the message with the specified ID. | 492 """Add or update the message with the specified ID. |
485 | 493 |
486 >>> catalog = Catalog() | 494 >>> catalog = Catalog() |
487 >>> catalog.add(u'foo') | 495 >>> catalog.add(u'foo') |
488 >>> catalog[u'foo'] | 496 >>> catalog[u'foo'] |
499 :param flags: a set or sequence of flags | 507 :param flags: a set or sequence of flags |
500 :param auto_comments: a sequence of automatic comments | 508 :param auto_comments: a sequence of automatic comments |
501 :param user_comments: a sequence of user comments | 509 :param user_comments: a sequence of user comments |
502 :param previous_id: the previous message ID, or a ``(singular, plural)`` | 510 :param previous_id: the previous message ID, or a ``(singular, plural)`` |
503 tuple for pluralizable messages | 511 tuple for pluralizable messages |
512 :param lineno: the line number on which the msgid line was found in the | |
513 PO file, if any | |
504 """ | 514 """ |
505 self[id] = Message(id, string, list(locations), flags, auto_comments, | 515 self[id] = Message(id, string, list(locations), flags, auto_comments, |
506 user_comments, previous_id) | 516 user_comments, previous_id, lineno=lineno) |
517 | |
518 def check(self): | |
519 """Run various validation checks on the translations in the catalog. | |
520 | |
521 For every message which fails validation, this method yield a | |
522 ``(message, errors)`` tuple, where ``message`` is the `Message` object | |
523 and ``errors`` is a sequence of `TranslationError` objects. | |
524 | |
525 :rtype: ``iterator`` | |
526 """ | |
527 checkers = [] | |
528 from pkg_resources import working_set | |
529 for entry_point in working_set.iter_entry_points('babel.checkers'): | |
530 checkers.append(entry_point.load()) | |
531 | |
532 for message in self._messages.values(): | |
533 errors = [] | |
534 for checker in checkers: | |
535 try: | |
536 checker(self, message) | |
537 except TranslationError, e: | |
538 errors.append(e) | |
539 if errors: | |
540 yield message, errors | |
507 | 541 |
508 def update(self, template, no_fuzzy_matching=False): | 542 def update(self, template, no_fuzzy_matching=False): |
509 """Update the catalog based on the given template catalog. | 543 """Update the catalog based on the given template catalog. |
510 | 544 |
511 >>> from babel.messages import Catalog | 545 >>> from babel.messages import Catalog |