Mercurial > babel > old > mirror
comparison babel/messages/catalog.py @ 69:1d8e81bfedf9
Enhance catalog to also manage the MIME headers.
author | cmlenz |
---|---|
date | Fri, 08 Jun 2007 15:32:06 +0000 |
parents | d1a7425739d3 |
children | 620fdd25657a |
comparison
equal
deleted
inserted
replaced
68:22e30e5a6736 | 69:1d8e81bfedf9 |
---|---|
11 # individuals. For the exact contribution history, see the revision | 11 # individuals. For the exact contribution history, see the revision |
12 # history and logs, available at http://babel.edgewall.org/log/. | 12 # history and logs, available at http://babel.edgewall.org/log/. |
13 | 13 |
14 """Data structures for message catalogs.""" | 14 """Data structures for message catalogs.""" |
15 | 15 |
16 from datetime import datetime | |
16 import re | 17 import re |
17 try: | 18 try: |
18 set | 19 set |
19 except NameError: | 20 except NameError: |
20 from sets import Set as set | 21 from sets import Set as set |
21 | 22 import time |
23 | |
24 from babel import __version__ as VERSION | |
22 from babel.core import Locale | 25 from babel.core import Locale |
23 from babel.util import odict | 26 from babel.messages.plurals import PLURALS |
27 from babel.util import odict, UTC | |
24 | 28 |
25 __all__ = ['Message', 'Catalog'] | 29 __all__ = ['Message', 'Catalog'] |
26 __docformat__ = 'restructuredtext en' | 30 __docformat__ = 'restructuredtext en' |
27 | 31 |
28 PYTHON_FORMAT = re.compile(r'\%(\([\w]+\))?[diouxXeEfFgGcrs]').search | 32 PYTHON_FORMAT = re.compile(r'\%(\([\w]+\))?[diouxXeEfFgGcrs]').search |
43 """ | 47 """ |
44 self.id = id | 48 self.id = id |
45 self.string = string | 49 self.string = string |
46 self.locations = locations | 50 self.locations = locations |
47 self.flags = set(flags) | 51 self.flags = set(flags) |
48 if self.python_format: | 52 if id and self.python_format: |
49 self.flags.add('python-format') | 53 self.flags.add('python-format') |
50 else: | 54 else: |
51 self.flags.discard('python-format') | 55 self.flags.discard('python-format') |
52 | 56 |
53 def __repr__(self): | 57 def __repr__(self): |
54 return '<%s %r>' % (type(self).__name__, self.id) | 58 return '<%s %r>' % (type(self).__name__, self.id) |
59 | |
60 def fuzzy(self): | |
61 return 'fuzzy' in self.flags | |
62 fuzzy = property(fuzzy, doc="""\ | |
63 Whether the translation is fuzzy. | |
64 | |
65 >>> Message('foo').fuzzy | |
66 False | |
67 >>> Message('foo', 'foo', flags=['fuzzy']).fuzzy | |
68 True | |
69 | |
70 :type: `bool` | |
71 """) | |
55 | 72 |
56 def pluralizable(self): | 73 def pluralizable(self): |
57 return isinstance(self.id, (list, tuple)) | 74 return isinstance(self.id, (list, tuple)) |
58 pluralizable = property(pluralizable, doc="""\ | 75 pluralizable = property(pluralizable, doc="""\ |
59 Whether the message is plurizable. | 76 Whether the message is plurizable. |
84 | 101 |
85 | 102 |
86 class Catalog(object): | 103 class Catalog(object): |
87 """Representation a message catalog.""" | 104 """Representation a message catalog.""" |
88 | 105 |
89 def __init__(self, domain=None, locale=None): | 106 def __init__(self, locale=None, domain=None, project=None, version=None, |
107 creation_date=None, revision_date=None, last_translator=None): | |
90 """Initialize the catalog object. | 108 """Initialize the catalog object. |
91 | 109 |
92 :param domain: the message domain | 110 :param domain: the message domain |
93 :param locale: the locale identifier or `Locale` object, or `None` | 111 :param locale: the locale identifier or `Locale` object, or `None` |
94 if the catalog is not bound to a locale (which basically | 112 if the catalog is not bound to a locale (which basically |
96 """ | 114 """ |
97 self.domain = domain #: the message domain | 115 self.domain = domain #: the message domain |
98 if locale: | 116 if locale: |
99 locale = Locale.parse(locale) | 117 locale = Locale.parse(locale) |
100 self.locale = locale #: the locale or `None` | 118 self.locale = locale #: the locale or `None` |
101 self.messages = odict() #: the actual `Message` entries by ID | 119 self._messages = odict() |
120 | |
121 self.project = project or 'PROJECT' #: the project name | |
122 self.version = version or 'VERSION' #: the project version | |
123 | |
124 if creation_date is None: | |
125 creation_date = time.localtime() | |
126 elif isinstance(creation_date, datetime): | |
127 if creation_date.tzinfo is None: | |
128 creation_date = creation_date.replace(tzinfo=UTC) | |
129 creation_date = creation_date.timetuple() | |
130 self.creation_date = creation_date #: creation date of the template | |
131 if revision_date is None: | |
132 revision_date = time.localtime() | |
133 elif isinstance(revision_date, datetime): | |
134 if revision_date.tzinfo is None: | |
135 revision_date = revision_date.replace(tzinfo=UTC) | |
136 revision_date = revision_date.timetuple() | |
137 self.revision_date = revision_date #: last revision date of the catalog | |
138 self.last_translator = last_translator #: last translator name + email | |
139 | |
140 def headers(self): | |
141 headers = [] | |
142 headers.append(('Project-Id-Version', | |
143 '%s %s' % (self.project, self.version))) | |
144 headers.append(('POT-Creation-Date', | |
145 time.strftime('%Y-%m-%d %H:%M%z', self.creation_date))) | |
146 if self.locale is None: | |
147 headers.append(('PO-Revision-Date', 'YEAR-MO-DA HO:MI+ZONE')) | |
148 headers.append(('Last-Translator', 'FULL NAME <EMAIL@ADDRESS>')) | |
149 headers.append(('Language-Team', 'LANGUAGE <LL@li.org>')) | |
150 else: | |
151 headers.append(('PO-Revision-Date', | |
152 time.strftime('%Y-%m-%d %H:%M%z', self.revision_date))) | |
153 headers.append(('Last-Translator', self.last_translator)) | |
154 headers.append(('Language-Team', '%s <LL@li.org>' % self.locale)) | |
155 headers.append(('Plural-Forms', self.plural_forms)) | |
156 headers.append(('MIME-Version', '1.0')) | |
157 headers.append(('Content-Type', 'text/plain; charset=utf-8')) | |
158 headers.append(('Content-Transfer-Encoding', '8bit')) | |
159 headers.append(('Generated-By', 'Babel %s' % VERSION)) | |
160 return headers | |
161 headers = property(headers, doc="""\ | |
162 The MIME headers of the catalog, used for the special ``msgid ""`` entry. | |
163 | |
164 The behavior of this property changes slightly depending on whether a locale | |
165 is set or not, the latter indicating that the catalog is actually a template | |
166 for actual translations. | |
167 | |
168 Here's an example of the output for such a catalog template: | |
169 | |
170 >>> catalog = Catalog(project='Foobar', version='1.0', | |
171 ... creation_date=datetime(1990, 4, 1, 15, 30)) | |
172 >>> for name, value in catalog.headers: | |
173 ... print '%s: %s' % (name, value) | |
174 Project-Id-Version: Foobar 1.0 | |
175 POT-Creation-Date: 1990-04-01 15:30+0000 | |
176 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE | |
177 Last-Translator: FULL NAME <EMAIL@ADDRESS> | |
178 Language-Team: LANGUAGE <LL@li.org> | |
179 Plural-Forms: nplurals=INTEGER; plural=EXPRESSION | |
180 MIME-Version: 1.0 | |
181 Content-Type: text/plain; charset=utf-8 | |
182 Content-Transfer-Encoding: 8bit | |
183 Generated-By: Babel ... | |
184 | |
185 And here's an example of the output when the locale is set: | |
186 | |
187 >>> catalog = Catalog(locale='de_DE', project='Foobar', version='1.0', | |
188 ... creation_date=datetime(1990, 4, 1, 15, 30), | |
189 ... revision_date=datetime(1990, 8, 3, 12, 0), | |
190 ... last_translator='John Doe <jd@example.com>') | |
191 >>> for name, value in catalog.headers: | |
192 ... print '%s: %s' % (name, value) | |
193 Project-Id-Version: Foobar 1.0 | |
194 POT-Creation-Date: 1990-04-01 15:30+0000 | |
195 PO-Revision-Date: 1990-08-03 12:00+0000 | |
196 Last-Translator: John Doe <jd@example.com> | |
197 Language-Team: de_DE <LL@li.org> | |
198 Plural-Forms: nplurals=2; plural=(n != 1) | |
199 MIME-Version: 1.0 | |
200 Content-Type: text/plain; charset=utf-8 | |
201 Content-Transfer-Encoding: 8bit | |
202 Generated-By: Babel ... | |
203 | |
204 :type: `list` | |
205 """) | |
206 | |
207 def plural_forms(self): | |
208 num, expr = ('INTEGER', 'EXPRESSION') | |
209 if self.locale: | |
210 if str(self.locale) in PLURALS: | |
211 num, expr = PLURALS[str(self.locale)] | |
212 elif self.locale.language in PLURALS: | |
213 num, expr = PLURALS[self.locale.language] | |
214 return 'nplurals=%s; plural=%s' % (num, expr) | |
215 plural_forms = property(plural_forms, doc="""\ | |
216 Return the plural forms declaration for the locale. | |
217 | |
218 >>> Catalog(locale='en_US').plural_forms | |
219 'nplurals=2; plural=(n != 1)' | |
220 >>> Catalog(locale='pt_BR').plural_forms | |
221 'nplurals=2; plural=(n > 1)' | |
222 | |
223 :type: `str` | |
224 """) | |
225 | |
226 def __contains__(self, id): | |
227 """Return whether the catalog has a message with the specified ID.""" | |
228 return id in self._messages | |
102 | 229 |
103 def __iter__(self): | 230 def __iter__(self): |
104 """Iterates through all the entries in the catalog, in the order they | 231 """Iterates through all the entries in the catalog, in the order they |
105 were added, yielding a `Message` object for every entry. | 232 were added, yielding a `Message` object for every entry. |
106 | 233 |
107 :rtype: ``iterator`` | 234 :rtype: ``iterator`` |
108 """ | 235 """ |
109 for id in self.messages: | 236 buf = [] |
110 yield self.messages[id] | 237 for name, value in self.headers: |
238 buf.append('%s: %s' % (name, value)) | |
239 yield Message('', '\n'.join(buf), flags=set(['fuzzy'])) | |
240 for id in self._messages: | |
241 yield self._messages[id] | |
111 | 242 |
112 def __repr__(self): | 243 def __repr__(self): |
113 locale = '' | 244 locale = '' |
114 if self.locale: | 245 if self.locale: |
115 locale = ' %s' % self.locale | 246 locale = ' %s' % self.locale |
116 return '<%s %r%s>' % (type(self).__name__, self.domain, locale) | 247 return '<%s %r%s>' % (type(self).__name__, self.domain, locale) |
117 | 248 |
118 def __delitem__(self, id): | 249 def __delitem__(self, id): |
119 """Delete the message with the specified ID.""" | 250 """Delete the message with the specified ID.""" |
120 if id in self.messaages: | 251 if id in self._messages: |
121 del self.messages[id] | 252 del self._messages[id] |
122 | 253 |
123 def __getitem__(self, id): | 254 def __getitem__(self, id): |
124 """Return the message with the specified ID. | 255 """Return the message with the specified ID. |
125 | 256 |
126 :param id: the message ID | 257 :param id: the message ID |
127 :return: the message with the specified ID, or `None` if no such message | 258 :return: the message with the specified ID, or `None` if no such message |
128 is in the catalog | 259 is in the catalog |
129 :rytpe: `Message` | 260 :rtype: `Message` |
130 """ | 261 """ |
131 return self.messages.get(id) | 262 return self._messages.get(id) |
132 | 263 |
133 def __setitem__(self, id, message): | 264 def __setitem__(self, id, message): |
134 """Add or update the message with the specified ID. | 265 """Add or update the message with the specified ID. |
135 | 266 |
136 >>> catalog = Catalog() | 267 >>> catalog = Catalog() |
151 | 282 |
152 :param id: the message ID | 283 :param id: the message ID |
153 :param message: the `Message` object | 284 :param message: the `Message` object |
154 """ | 285 """ |
155 assert isinstance(message, Message), 'expected a Message object' | 286 assert isinstance(message, Message), 'expected a Message object' |
156 current = self.messages.get(id) | 287 current = self._messages.get(id) |
157 if current: | 288 if current: |
158 assert current.string == message.string, 'translation mismatch' | 289 assert current.string == message.string, 'translation mismatch' |
159 current.locations.extend(message.locations) | 290 current.locations.extend(message.locations) |
160 current.flags |= message.flags | 291 current.flags |= message.flags |
161 message = current | 292 message = current |
162 else: | 293 else: |
163 if isinstance(id, (list, tuple)): | 294 if isinstance(id, (list, tuple)): |
164 singular, plural = id | 295 singular, plural = id |
165 id = singular | 296 id = singular |
166 self.messages[id] = message | 297 self._messages[id] = message |
167 | 298 |
168 def add(self, id, string=None, locations=(), flags=()): | 299 def add(self, id, string=None, locations=(), flags=()): |
169 """Add or update the message with the specified ID. | 300 """Add or update the message with the specified ID. |
170 | 301 |
171 >>> catalog = Catalog() | 302 >>> catalog = Catalog() |