Mercurial > genshi > mirror
annotate genshi/filters/i18n.py @ 854:4d9bef447df9 trunk
More work on reducing the size of the diff produced by 2to3.
author | cmlenz |
---|---|
date | Tue, 10 Nov 2009 22:53:21 +0000 |
parents | f33ecf3c319e |
children | 21308bd343b8 |
rev | line source |
---|---|
531 | 1 # -*- coding: utf-8 -*- |
2 # | |
850
e8f596c726c4
Update copyright years on files changed due to advanced-i18n merge.
cmlenz
parents:
849
diff
changeset
|
3 # Copyright (C) 2007-2009 Edgewall Software |
531 | 4 # All rights reserved. |
5 # | |
6 # This software is licensed as described in the file COPYING, which | |
7 # you should have received as part of this distribution. The terms | |
8 # are also available at http://genshi.edgewall.org/wiki/License. | |
9 # | |
10 # This software consists of voluntary contributions made by many | |
11 # individuals. For the exact contribution history, see the revision | |
12 # history and logs, available at http://genshi.edgewall.org/log/. | |
13 | |
849 | 14 """Directives and utilities for internationalization and localization of |
15 templates. | |
576 | 16 |
17 :since: version 0.4 | |
849 | 18 :note: Directives support added since version 0.6 |
576 | 19 """ |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
20 |
788
31432f30a6fb
Change `Translator` class to accept either a `gettext`-style function, or an object compatible with the `NullTranslations` / `GNUTranslations` interface.
cmlenz
parents:
787
diff
changeset
|
21 from gettext import NullTranslations |
849 | 22 import os |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
23 import re |
788
31432f30a6fb
Change `Translator` class to accept either a `gettext`-style function, or an object compatible with the `NullTranslations` / `GNUTranslations` interface.
cmlenz
parents:
787
diff
changeset
|
24 from types import FunctionType |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
25 |
560
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
26 from genshi.core import Attrs, Namespace, QName, START, END, TEXT, START_NS, \ |
849 | 27 END_NS, XML_NAMESPACE, _ensure, StreamEventKind |
794
f9e23d472a6e
Merged AST branch back into trunk. Most of this code was written by Marcin Kurczych for his Google Summer of Code 2008 project. The merge of this branch means that Genshi now uses the native `_ast` module on Python >= 2.5, and an emulation thereof on Python 2.4. This replaces the usage of the `compiler` package, which was deprecated in Python 2.6 and removed in Python 3.0. Another effect is that Genshi now runs on Google AppEngine (although performance is bad due to the lack of template caching).
cmlenz
parents:
790
diff
changeset
|
28 from genshi.template.eval import _ast |
790
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
29 from genshi.template.base import DirectiveFactory, EXPR, SUB, _apply_directives |
849 | 30 from genshi.template.directives import Directive, StripDirective |
528
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
31 from genshi.template.markup import MarkupTemplate, EXEC |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
32 |
528
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
33 __all__ = ['Translator', 'extract'] |
501
5e7604c2d60d
Added new markup transformation filter contributed by Alec Thomas (#122). This provides gorgeous jQuery-inspired stream transformation capabilities based on XPath expressions.
cmlenz
parents:
493
diff
changeset
|
34 __docformat__ = 'restructuredtext en' |
5e7604c2d60d
Added new markup transformation filter contributed by Alec Thomas (#122). This provides gorgeous jQuery-inspired stream transformation capabilities based on XPath expressions.
cmlenz
parents:
493
diff
changeset
|
35 |
849 | 36 |
560
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
37 I18N_NAMESPACE = Namespace('http://genshi.edgewall.org/i18n') |
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
38 |
849 | 39 MSGBUF = StreamEventKind('MSGBUF') |
40 SUB_START = StreamEventKind('SUB_START') | |
41 SUB_END = StreamEventKind('SUB_END') | |
42 | |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
43 |
849 | 44 class I18NDirective(Directive): |
45 """Simple interface for i18n directives to support messages extraction.""" | |
790
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
46 |
849 | 47 def __call__(self, stream, directives, ctxt, **vars): |
48 return _apply_directives(stream, directives, ctxt, vars) | |
790
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
49 |
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
50 |
849 | 51 class ExtractableI18NDirective(I18NDirective): |
52 """Simple interface for directives to support messages extraction.""" | |
790
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
53 |
849 | 54 def extract(self, stream, comment_stack): |
55 raise NotImplementedError | |
56 | |
57 | |
58 class CommentDirective(I18NDirective): | |
59 """Implementation of the ``i18n:comment`` template directive which adds | |
60 translation comments. | |
61 | |
62 >>> tmpl = MarkupTemplate('''<html xmlns:i18n="http://genshi.edgewall.org/i18n"> | |
63 ... <p i18n:comment="As in Foo Bar">Foo</p> | |
64 ... </html>''') | |
65 >>> translator = Translator() | |
66 >>> translator.setup(tmpl) | |
67 >>> list(translator.extract(tmpl.stream)) | |
68 [(2, None, u'Foo', [u'As in Foo Bar'])] | |
69 """ | |
70 __slots__ = ['comment'] | |
71 | |
72 def __init__(self, value, template, hints=None, namespaces=None, | |
73 lineno=-1, offset=-1): | |
74 Directive.__init__(self, None, template, namespaces, lineno, offset) | |
75 self.comment = value | |
76 | |
77 | |
78 class MsgDirective(ExtractableI18NDirective): | |
79 r"""Implementation of the ``i18n:msg`` directive which marks inner content | |
80 as translatable. Consider the following examples: | |
81 | |
82 >>> tmpl = MarkupTemplate('''<html xmlns:i18n="http://genshi.edgewall.org/i18n"> | |
83 ... <div i18n:msg=""> | |
84 ... <p>Foo</p> | |
85 ... <p>Bar</p> | |
86 ... </div> | |
87 ... <p i18n:msg="">Foo <em>bar</em>!</p> | |
88 ... </html>''') | |
89 | |
90 >>> translator = Translator() | |
91 >>> translator.setup(tmpl) | |
92 >>> list(translator.extract(tmpl.stream)) | |
93 [(2, None, u'[1:Foo]\n [2:Bar]', []), (6, None, u'Foo [1:bar]!', [])] | |
853
f33ecf3c319e
Convert a bunch of print statements to py3k compatible syntax.
cmlenz
parents:
850
diff
changeset
|
94 >>> print(tmpl.generate().render()) |
849 | 95 <html> |
96 <div><p>Foo</p> | |
97 <p>Bar</p></div> | |
98 <p>Foo <em>bar</em>!</p> | |
99 </html> | |
100 | |
101 >>> tmpl = MarkupTemplate('''<html xmlns:i18n="http://genshi.edgewall.org/i18n"> | |
102 ... <div i18n:msg="fname, lname"> | |
103 ... <p>First Name: ${fname}</p> | |
104 ... <p>Last Name: ${lname}</p> | |
105 ... </div> | |
106 ... <p i18n:msg="">Foo <em>bar</em>!</p> | |
107 ... </html>''') | |
108 >>> translator.setup(tmpl) | |
109 >>> list(translator.extract(tmpl.stream)) #doctest: +NORMALIZE_WHITESPACE | |
110 [(2, None, u'[1:First Name: %(fname)s]\n [2:Last Name: %(lname)s]', []), | |
111 (6, None, u'Foo [1:bar]!', [])] | |
112 | |
113 >>> tmpl = MarkupTemplate('''<html xmlns:i18n="http://genshi.edgewall.org/i18n"> | |
114 ... <div i18n:msg="fname, lname"> | |
115 ... <p>First Name: ${fname}</p> | |
116 ... <p>Last Name: ${lname}</p> | |
117 ... </div> | |
118 ... <p i18n:msg="">Foo <em>bar</em>!</p> | |
119 ... </html>''') | |
120 >>> translator.setup(tmpl) | |
853
f33ecf3c319e
Convert a bunch of print statements to py3k compatible syntax.
cmlenz
parents:
850
diff
changeset
|
121 >>> print(tmpl.generate(fname='John', lname='Doe').render()) |
849 | 122 <html> |
123 <div><p>First Name: John</p> | |
124 <p>Last Name: Doe</p></div> | |
125 <p>Foo <em>bar</em>!</p> | |
126 </html> | |
127 | |
128 Starting and ending white-space is stripped of to make it simpler for | |
129 translators. Stripping it is not that important since it's on the html | |
130 source, the rendered output will remain the same. | |
131 """ | |
790
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
132 __slots__ = ['params'] |
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
133 |
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
134 def __init__(self, value, template, hints=None, namespaces=None, |
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
135 lineno=-1, offset=-1): |
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
136 Directive.__init__(self, None, template, namespaces, lineno, offset) |
849 | 137 self.params = [param.strip() for param in value.split(',') if param] |
138 | |
139 @classmethod | |
140 def attach(cls, template, stream, value, namespaces, pos): | |
141 if type(value) is dict: | |
142 value = value.get('params', '').strip() | |
143 return super(MsgDirective, cls).attach(template, stream, value.strip(), | |
144 namespaces, pos) | |
790
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
145 |
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
146 def __call__(self, stream, directives, ctxt, **vars): |
849 | 147 gettext = ctxt.get('_i18n.gettext') |
148 dgettext = ctxt.get('_i18n.dgettext') | |
149 if ctxt.get('_i18n.domain'): | |
854
4d9bef447df9
More work on reducing the size of the diff produced by 2to3.
cmlenz
parents:
853
diff
changeset
|
150 assert hasattr(dgettext, '__call__'), \ |
4d9bef447df9
More work on reducing the size of the diff produced by 2to3.
cmlenz
parents:
853
diff
changeset
|
151 'No domain gettext function passed' |
849 | 152 gettext = lambda msg: dgettext(ctxt.get('_i18n.domain'), msg) |
153 | |
154 def _generate(): | |
155 msgbuf = MessageBuffer(self) | |
156 previous = stream.next() | |
157 if previous[0] is START: | |
158 yield previous | |
159 else: | |
160 msgbuf.append(*previous) | |
161 previous = stream.next() | |
162 for kind, data, pos in stream: | |
163 msgbuf.append(*previous) | |
164 previous = kind, data, pos | |
165 if previous[0] is not END: | |
166 msgbuf.append(*previous) | |
167 previous = None | |
168 for event in msgbuf.translate(gettext(msgbuf.format())): | |
169 yield event | |
170 if previous: | |
171 yield previous | |
172 | |
173 return _apply_directives(_generate(), directives, ctxt, vars) | |
174 | |
175 def extract(self, stream, comment_stack): | |
176 msgbuf = MessageBuffer(self) | |
790
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
177 |
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
178 stream = iter(stream) |
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
179 previous = stream.next() |
849 | 180 if previous[0] is START: |
181 previous = stream.next() | |
790
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
182 for event in stream: |
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
183 msgbuf.append(*previous) |
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
184 previous = event |
849 | 185 if previous[0] is not END: |
186 msgbuf.append(*previous) | |
790
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
187 |
849 | 188 yield None, msgbuf.format(), comment_stack[-1:] |
189 | |
190 | |
191 class ChooseBranchDirective(I18NDirective): | |
192 __slots__ = ['params'] | |
193 | |
194 def __call__(self, stream, directives, ctxt, **vars): | |
195 self.params = ctxt.get('_i18n.choose.params', [])[:] | |
196 msgbuf = MessageBuffer(self) | |
197 | |
198 stream = iter(_apply_directives(stream, directives, ctxt, vars)) | |
199 yield stream.next() # the outer start tag | |
200 previous = stream.next() | |
201 for kind, data, pos in stream: | |
202 msgbuf.append(*previous) | |
203 previous = kind, data, pos | |
204 yield MSGBUF, (), -1 # the place holder for msgbuf output | |
205 yield previous # the outer end tag | |
206 ctxt['_i18n.choose.%s' % type(self).__name__] = msgbuf | |
207 | |
208 | |
209 def extract(self, stream, comment_stack, msgbuf): | |
210 stream = iter(stream) | |
211 previous = stream.next() | |
212 if previous[0] is START: | |
213 previous = stream.next() | |
214 for event in stream: | |
215 msgbuf.append(*previous) | |
216 previous = event | |
217 if previous[0] is not END: | |
218 msgbuf.append(*previous) | |
219 return msgbuf | |
220 | |
221 | |
222 class SingularDirective(ChooseBranchDirective): | |
223 """Implementation of the ``i18n:singular`` directive to be used with the | |
224 ``i18n:choose`` directive.""" | |
225 | |
226 | |
227 class PluralDirective(ChooseBranchDirective): | |
228 """Implementation of the ``i18n:plural`` directive to be used with the | |
229 ``i18n:choose`` directive.""" | |
230 | |
231 | |
232 class ChooseDirective(ExtractableI18NDirective): | |
233 """Implementation of the ``i18n:choose`` directive which provides plural | |
234 internationalisation of strings. | |
235 | |
236 This directive requires at least one parameter, the one which evaluates to | |
237 an integer which will allow to choose the plural/singular form. If you also | |
238 have expressions inside the singular and plural version of the string you | |
239 also need to pass a name for those parameters. Consider the following | |
240 examples: | |
241 | |
242 >>> tmpl = MarkupTemplate('''\ | |
243 <html xmlns:i18n="http://genshi.edgewall.org/i18n"> | |
244 ... <div i18n:choose="num; num"> | |
245 ... <p i18n:singular="">There is $num coin</p> | |
246 ... <p i18n:plural="">There are $num coins</p> | |
247 ... </div> | |
248 ... </html>''') | |
249 >>> translator = Translator() | |
250 >>> translator.setup(tmpl) | |
251 >>> list(translator.extract(tmpl.stream)) #doctest: +NORMALIZE_WHITESPACE | |
252 [(2, 'ngettext', (u'There is %(num)s coin', | |
253 u'There are %(num)s coins'), [])] | |
254 | |
255 >>> tmpl = MarkupTemplate('''\ | |
256 <html xmlns:i18n="http://genshi.edgewall.org/i18n"> | |
257 ... <div i18n:choose="num; num"> | |
258 ... <p i18n:singular="">There is $num coin</p> | |
259 ... <p i18n:plural="">There are $num coins</p> | |
260 ... </div> | |
261 ... </html>''') | |
262 >>> translator.setup(tmpl) | |
853
f33ecf3c319e
Convert a bunch of print statements to py3k compatible syntax.
cmlenz
parents:
850
diff
changeset
|
263 >>> print(tmpl.generate(num=1).render()) |
849 | 264 <html> |
265 <div> | |
266 <p>There is 1 coin</p> | |
267 </div> | |
268 </html> | |
853
f33ecf3c319e
Convert a bunch of print statements to py3k compatible syntax.
cmlenz
parents:
850
diff
changeset
|
269 >>> print(tmpl.generate(num=2).render()) |
849 | 270 <html> |
271 <div> | |
272 <p>There are 2 coins</p> | |
273 </div> | |
274 </html> | |
275 | |
276 When used as a directive and not as an attribute: | |
277 | |
278 >>> tmpl = MarkupTemplate('''\ | |
279 <html xmlns:i18n="http://genshi.edgewall.org/i18n"> | |
280 ... <i18n:choose numeral="num" params="num"> | |
281 ... <p i18n:singular="">There is $num coin</p> | |
282 ... <p i18n:plural="">There are $num coins</p> | |
283 ... </i18n:choose> | |
284 ... </html>''') | |
285 >>> translator.setup(tmpl) | |
286 >>> list(translator.extract(tmpl.stream)) #doctest: +NORMALIZE_WHITESPACE | |
287 [(2, 'ngettext', (u'There is %(num)s coin', | |
288 u'There are %(num)s coins'), [])] | |
289 """ | |
290 __slots__ = ['numeral', 'params'] | |
291 | |
292 def __init__(self, value, template, hints=None, namespaces=None, | |
293 lineno=-1, offset=-1): | |
294 Directive.__init__(self, None, template, namespaces, lineno, offset) | |
295 params = [v.strip() for v in value.split(';')] | |
296 self.numeral = self._parse_expr(params.pop(0), template, lineno, offset) | |
297 self.params = params and [name.strip() for name in | |
298 params[0].split(',') if name] or [] | |
299 | |
300 @classmethod | |
301 def attach(cls, template, stream, value, namespaces, pos): | |
302 if type(value) is dict: | |
303 numeral = value.get('numeral', '').strip() | |
304 assert numeral is not '', "at least pass the numeral param" | |
305 params = [v.strip() for v in value.get('params', '').split(',')] | |
306 value = '%s; ' % numeral + ', '.join(params) | |
307 return super(ChooseDirective, cls).attach(template, stream, value, | |
308 namespaces, pos) | |
309 | |
310 def __call__(self, stream, directives, ctxt, **vars): | |
311 ctxt.push({'_i18n.choose.params': self.params, | |
312 '_i18n.choose.SingularDirective': None, | |
313 '_i18n.choose.PluralDirective': None}) | |
314 | |
315 new_stream = [] | |
316 singular_stream = None | |
317 singular_msgbuf = None | |
318 plural_stream = None | |
319 plural_msgbuf = None | |
320 | |
321 ngettext = ctxt.get('_i18n.ungettext') | |
854
4d9bef447df9
More work on reducing the size of the diff produced by 2to3.
cmlenz
parents:
853
diff
changeset
|
322 assert hasattr(ngettext, '__call__'), 'No ngettext function available' |
849 | 323 dngettext = ctxt.get('_i18n.dngettext') |
324 if not dngettext: | |
325 dngettext = lambda d, s, p, n: ngettext(s, p, n) | |
326 | |
327 for kind, event, pos in stream: | |
328 if kind is SUB: | |
329 subdirectives, substream = event | |
330 if isinstance(subdirectives[0], | |
331 SingularDirective) and not singular_stream: | |
332 # Apply directives to update context | |
333 singular_stream = list(_apply_directives(substream, | |
334 subdirectives, | |
335 ctxt, vars)) | |
336 new_stream.append((MSGBUF, (), ('', -1))) # msgbuf place holder | |
337 singular_msgbuf = ctxt.get('_i18n.choose.SingularDirective') | |
338 elif isinstance(subdirectives[0], | |
339 PluralDirective) and not plural_stream: | |
340 # Apply directives to update context | |
341 plural_stream = list(_apply_directives(substream, | |
342 subdirectives, | |
343 ctxt, vars)) | |
344 plural_msgbuf = ctxt.get('_i18n.choose.PluralDirective') | |
345 else: | |
346 new_stream.append((kind, event, pos)) | |
347 else: | |
348 new_stream.append((kind, event, pos)) | |
349 | |
350 if ctxt.get('_i18n.domain'): | |
351 ngettext = lambda s, p, n: dngettext(ctxt.get('_i18n.domain'), | |
352 s, p, n) | |
353 | |
354 for kind, data, pos in new_stream: | |
355 if kind is MSGBUF: | |
356 for skind, sdata, spos in singular_stream: | |
357 if skind is MSGBUF: | |
358 translation = ngettext(singular_msgbuf.format(), | |
359 plural_msgbuf.format(), | |
360 self.numeral.evaluate(ctxt)) | |
361 for event in singular_msgbuf.translate(translation): | |
362 yield event | |
363 else: | |
364 yield skind, sdata, spos | |
365 else: | |
366 yield kind, data, pos | |
367 | |
368 ctxt.pop() | |
369 | |
370 def extract(self, stream, comment_stack): | |
371 stream = iter(stream) | |
372 previous = stream.next() | |
373 if previous is START: | |
374 stream.next() | |
375 | |
376 singular_msgbuf = MessageBuffer(self) | |
377 plural_msgbuf = MessageBuffer(self) | |
378 | |
379 for kind, event, pos in stream: | |
380 if kind is SUB: | |
381 subdirectives, substream = event | |
382 for subdirective in subdirectives: | |
383 if isinstance(subdirective, SingularDirective): | |
384 singular_msgbuf = subdirective.extract(substream, comment_stack, | |
385 singular_msgbuf) | |
386 elif isinstance(subdirective, PluralDirective): | |
387 plural_msgbuf = subdirective.extract(substream, comment_stack, | |
388 plural_msgbuf) | |
389 elif not isinstance(subdirective, StripDirective): | |
390 singular_msgbuf.append(kind, event, pos) | |
391 plural_msgbuf.append(kind, event, pos) | |
392 else: | |
393 singular_msgbuf.append(kind, event, pos) | |
394 plural_msgbuf.append(kind, event, pos) | |
395 | |
396 yield 'ngettext', \ | |
397 (singular_msgbuf.format(), plural_msgbuf.format()), \ | |
398 comment_stack[-1:] | |
399 | |
400 | |
401 class DomainDirective(I18NDirective): | |
402 """Implementation of the ``i18n:domain`` directive which allows choosing | |
403 another i18n domain(catalog) to translate from. | |
404 | |
405 >>> from genshi.filters.tests.i18n import DummyTranslations | |
406 >>> tmpl = MarkupTemplate('''\ | |
407 <html xmlns:i18n="http://genshi.edgewall.org/i18n"> | |
408 ... <p i18n:msg="">Bar</p> | |
409 ... <div i18n:domain="foo"> | |
410 ... <p i18n:msg="">FooBar</p> | |
411 ... <p>Bar</p> | |
412 ... <p i18n:domain="bar" i18n:msg="">Bar</p> | |
413 ... <p i18n:domain="">Bar</p> | |
414 ... </div> | |
415 ... <p>Bar</p> | |
416 ... </html>''') | |
417 | |
418 >>> translations = DummyTranslations({'Bar': 'Voh'}) | |
419 >>> translations.add_domain('foo', {'FooBar': 'BarFoo', 'Bar': 'foo_Bar'}) | |
420 >>> translations.add_domain('bar', {'Bar': 'bar_Bar'}) | |
421 >>> translator = Translator(translations) | |
422 >>> translator.setup(tmpl) | |
423 | |
853
f33ecf3c319e
Convert a bunch of print statements to py3k compatible syntax.
cmlenz
parents:
850
diff
changeset
|
424 >>> print(tmpl.generate().render()) |
849 | 425 <html> |
426 <p>Voh</p> | |
427 <div> | |
428 <p>BarFoo</p> | |
429 <p>foo_Bar</p> | |
430 <p>bar_Bar</p> | |
431 <p>Voh</p> | |
432 </div> | |
433 <p>Voh</p> | |
434 </html> | |
435 """ | |
436 __slots__ = ['domain'] | |
437 | |
438 def __init__(self, value, template, hints=None, namespaces=None, | |
439 lineno=-1, offset=-1): | |
440 Directive.__init__(self, None, template, namespaces, lineno, offset) | |
441 self.domain = value and value.strip() or '__DEFAULT__' | |
442 | |
443 @classmethod | |
444 def attach(cls, template, stream, value, namespaces, pos): | |
445 if type(value) is dict: | |
446 value = value.get('name') | |
447 return super(DomainDirective, cls).attach(template, stream, value, | |
448 namespaces, pos) | |
449 | |
450 def __call__(self, stream, directives, ctxt, **vars): | |
451 ctxt.push({'_i18n.domain': self.domain}) | |
452 for event in _apply_directives(stream, directives, ctxt, vars): | |
790
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
453 yield event |
849 | 454 ctxt.pop() |
790
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
455 |
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
456 |
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
457 class Translator(DirectiveFactory): |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
458 """Can extract and translate localizable strings from markup streams and |
450
94601511cd68
Extend the I18n extraction to also yield function names if applicable.
cmlenz
parents:
448
diff
changeset
|
459 templates. |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
460 |
849 | 461 For example, assume the following template: |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
462 |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
463 >>> tmpl = MarkupTemplate('''<html xmlns:py="http://genshi.edgewall.org/"> |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
464 ... <head> |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
465 ... <title>Example</title> |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
466 ... </head> |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
467 ... <body> |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
468 ... <h1>Example</h1> |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
469 ... <p>${_("Hello, %(name)s") % dict(name=username)}</p> |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
470 ... </body> |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
471 ... </html>''', filename='example.html') |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
472 |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
473 For demonstration, we define a dummy ``gettext``-style function with a |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
474 hard-coded translation table, and pass that to the `Translator` initializer: |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
475 |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
476 >>> def pseudo_gettext(string): |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
477 ... return { |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
478 ... 'Example': 'Beispiel', |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
479 ... 'Hello, %(name)s': 'Hallo, %(name)s' |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
480 ... }[string] |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
481 >>> translator = Translator(pseudo_gettext) |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
482 |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
483 Next, the translator needs to be prepended to any already defined filters |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
484 on the template: |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
485 |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
486 >>> tmpl.filters.insert(0, translator) |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
487 |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
488 When generating the template output, our hard-coded translations should be |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
489 applied as expected: |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
490 |
853
f33ecf3c319e
Convert a bunch of print statements to py3k compatible syntax.
cmlenz
parents:
850
diff
changeset
|
491 >>> print(tmpl.generate(username='Hans', _=pseudo_gettext)) |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
492 <html> |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
493 <head> |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
494 <title>Beispiel</title> |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
495 </head> |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
496 <body> |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
497 <h1>Beispiel</h1> |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
498 <p>Hallo, Hans</p> |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
499 </body> |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
500 </html> |
849 | 501 |
522
082535e5087c
The I18n filter now skips the content of elements that have an `xml:lang` attribute with a fixed string value. Basically, `xml:lang` can now be used as a flag to mark specific sections as not needing localization.
cmlenz
parents:
501
diff
changeset
|
502 Note that elements defining ``xml:lang`` attributes that do not contain |
082535e5087c
The I18n filter now skips the content of elements that have an `xml:lang` attribute with a fixed string value. Basically, `xml:lang` can now be used as a flag to mark specific sections as not needing localization.
cmlenz
parents:
501
diff
changeset
|
503 variable expressions are ignored by this filter. That can be used to |
082535e5087c
The I18n filter now skips the content of elements that have an `xml:lang` attribute with a fixed string value. Basically, `xml:lang` can now be used as a flag to mark specific sections as not needing localization.
cmlenz
parents:
501
diff
changeset
|
504 exclude specific parts of a template from being extracted and translated. |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
505 """ |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
506 |
790
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
507 directives = [ |
849 | 508 ('domain', DomainDirective), |
790
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
509 ('comment', CommentDirective), |
849 | 510 ('msg', MsgDirective), |
511 ('choose', ChooseDirective), | |
512 ('singular', SingularDirective), | |
513 ('plural', PluralDirective) | |
790
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
514 ] |
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
515 |
450
94601511cd68
Extend the I18n extraction to also yield function names if applicable.
cmlenz
parents:
448
diff
changeset
|
516 IGNORE_TAGS = frozenset([ |
94601511cd68
Extend the I18n extraction to also yield function names if applicable.
cmlenz
parents:
448
diff
changeset
|
517 QName('script'), QName('http://www.w3.org/1999/xhtml}script'), |
94601511cd68
Extend the I18n extraction to also yield function names if applicable.
cmlenz
parents:
448
diff
changeset
|
518 QName('style'), QName('http://www.w3.org/1999/xhtml}style') |
94601511cd68
Extend the I18n extraction to also yield function names if applicable.
cmlenz
parents:
448
diff
changeset
|
519 ]) |
849 | 520 INCLUDE_ATTRS = frozenset([ |
521 'abbr', 'alt', 'label', 'prompt', 'standby', 'summary', 'title' | |
522 ]) | |
790
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
523 NAMESPACE = I18N_NAMESPACE |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
524 |
788
31432f30a6fb
Change `Translator` class to accept either a `gettext`-style function, or an object compatible with the `NullTranslations` / `GNUTranslations` interface.
cmlenz
parents:
787
diff
changeset
|
525 def __init__(self, translate=NullTranslations(), ignore_tags=IGNORE_TAGS, |
594
2bbaa61b328f
Add option to I18n filter to only extract strings in gettext function calls.
cmlenz
parents:
576
diff
changeset
|
526 include_attrs=INCLUDE_ATTRS, extract_text=True): |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
527 """Initialize the translator. |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
528 |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
529 :param translate: the translation function, for example ``gettext`` or |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
530 ``ugettext``. |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
531 :param ignore_tags: a set of tag names that should not be localized |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
532 :param include_attrs: a set of attribute names should be localized |
594
2bbaa61b328f
Add option to I18n filter to only extract strings in gettext function calls.
cmlenz
parents:
576
diff
changeset
|
533 :param extract_text: whether the content of text nodes should be |
2bbaa61b328f
Add option to I18n filter to only extract strings in gettext function calls.
cmlenz
parents:
576
diff
changeset
|
534 extracted, or only text in explicit ``gettext`` |
2bbaa61b328f
Add option to I18n filter to only extract strings in gettext function calls.
cmlenz
parents:
576
diff
changeset
|
535 function calls |
849 | 536 |
788
31432f30a6fb
Change `Translator` class to accept either a `gettext`-style function, or an object compatible with the `NullTranslations` / `GNUTranslations` interface.
cmlenz
parents:
787
diff
changeset
|
537 :note: Changed in 0.6: the `translate` parameter can now be either |
31432f30a6fb
Change `Translator` class to accept either a `gettext`-style function, or an object compatible with the `NullTranslations` / `GNUTranslations` interface.
cmlenz
parents:
787
diff
changeset
|
538 a ``gettext``-style function, or an object compatible with the |
31432f30a6fb
Change `Translator` class to accept either a `gettext`-style function, or an object compatible with the `NullTranslations` / `GNUTranslations` interface.
cmlenz
parents:
787
diff
changeset
|
539 ``NullTransalations`` or ``GNUTranslations`` interface |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
540 """ |
450
94601511cd68
Extend the I18n extraction to also yield function names if applicable.
cmlenz
parents:
448
diff
changeset
|
541 self.translate = translate |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
542 self.ignore_tags = ignore_tags |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
543 self.include_attrs = include_attrs |
594
2bbaa61b328f
Add option to I18n filter to only extract strings in gettext function calls.
cmlenz
parents:
576
diff
changeset
|
544 self.extract_text = extract_text |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
545 |
790
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
546 def __call__(self, stream, ctxt=None, search_text=True): |
448 | 547 """Translate any localizable strings in the given stream. |
548 | |
549 This function shouldn't be called directly. Instead, an instance of | |
550 the `Translator` class should be registered as a filter with the | |
551 `Template` or the `TemplateLoader`, or applied as a regular stream | |
552 filter. If used as a template filter, it should be inserted in front of | |
553 all the default filters. | |
554 | |
555 :param stream: the markup event stream | |
556 :param ctxt: the template context (not used) | |
557 :param search_text: whether text nodes should be translated (used | |
558 internally) | |
559 :return: the localized stream | |
560 """ | |
450
94601511cd68
Extend the I18n extraction to also yield function names if applicable.
cmlenz
parents:
448
diff
changeset
|
561 ignore_tags = self.ignore_tags |
94601511cd68
Extend the I18n extraction to also yield function names if applicable.
cmlenz
parents:
448
diff
changeset
|
562 include_attrs = self.include_attrs |
790
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
563 skip = 0 |
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
564 xml_lang = XML_NAMESPACE['lang'] |
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
565 |
788
31432f30a6fb
Change `Translator` class to accept either a `gettext`-style function, or an object compatible with the `NullTranslations` / `GNUTranslations` interface.
cmlenz
parents:
787
diff
changeset
|
566 if type(self.translate) is FunctionType: |
31432f30a6fb
Change `Translator` class to accept either a `gettext`-style function, or an object compatible with the `NullTranslations` / `GNUTranslations` interface.
cmlenz
parents:
787
diff
changeset
|
567 gettext = self.translate |
849 | 568 if ctxt: |
569 ctxt['_i18n.gettext'] = gettext | |
788
31432f30a6fb
Change `Translator` class to accept either a `gettext`-style function, or an object compatible with the `NullTranslations` / `GNUTranslations` interface.
cmlenz
parents:
787
diff
changeset
|
570 else: |
31432f30a6fb
Change `Translator` class to accept either a `gettext`-style function, or an object compatible with the `NullTranslations` / `GNUTranslations` interface.
cmlenz
parents:
787
diff
changeset
|
571 gettext = self.translate.ugettext |
849 | 572 try: |
573 dgettext = self.translate.dugettext | |
574 except AttributeError: | |
575 dgettext = lambda x, y: gettext(y) | |
576 ngettext = self.translate.ungettext | |
577 try: | |
578 dngettext = self.translate.dungettext | |
579 except AttributeError: | |
580 dngettext = lambda d, s, p, n: ngettext(s, p, n) | |
581 | |
582 if ctxt: | |
583 ctxt['_i18n.gettext'] = gettext | |
584 ctxt['_i18n.ugettext'] = gettext | |
585 ctxt['_i18n.dgettext'] = dgettext | |
586 ctxt['_i18n.ngettext'] = ngettext | |
587 ctxt['_i18n.ungettext'] = ngettext | |
588 ctxt['_i18n.dngettext'] = dngettext | |
787
d7366797440f
Add support for supplying comments on localizable messages in the i18n filter. Based on patch by Pedro Algarvio on #129.
cmlenz
parents:
785
diff
changeset
|
589 |
790
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
590 extract_text = self.extract_text |
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
591 if not extract_text: |
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
592 search_text = False |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
593 |
849 | 594 if ctxt and ctxt.get('_i18n.domain'): |
595 old_gettext = gettext | |
596 gettext = lambda msg: dgettext(ctxt.get('_i18n.domain'), msg) | |
597 | |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
598 for kind, data, pos in stream: |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
599 |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
600 # skip chunks that should not be localized |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
601 if skip: |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
602 if kind is START: |
522
082535e5087c
The I18n filter now skips the content of elements that have an `xml:lang` attribute with a fixed string value. Basically, `xml:lang` can now be used as a flag to mark specific sections as not needing localization.
cmlenz
parents:
501
diff
changeset
|
603 skip += 1 |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
604 elif kind is END: |
522
082535e5087c
The I18n filter now skips the content of elements that have an `xml:lang` attribute with a fixed string value. Basically, `xml:lang` can now be used as a flag to mark specific sections as not needing localization.
cmlenz
parents:
501
diff
changeset
|
605 skip -= 1 |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
606 yield kind, data, pos |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
607 continue |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
608 |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
609 # handle different events that can be localized |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
610 if kind is START: |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
611 tag, attrs = data |
522
082535e5087c
The I18n filter now skips the content of elements that have an `xml:lang` attribute with a fixed string value. Basically, `xml:lang` can now be used as a flag to mark specific sections as not needing localization.
cmlenz
parents:
501
diff
changeset
|
612 if tag in self.ignore_tags or \ |
082535e5087c
The I18n filter now skips the content of elements that have an `xml:lang` attribute with a fixed string value. Basically, `xml:lang` can now be used as a flag to mark specific sections as not needing localization.
cmlenz
parents:
501
diff
changeset
|
613 isinstance(attrs.get(xml_lang), basestring): |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
614 skip += 1 |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
615 yield kind, data, pos |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
616 continue |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
617 |
493
3f6582a5a4a5
Fix another bug in the translation filter: translated attributes were getting added instead of replaced.
cmlenz
parents:
485
diff
changeset
|
618 new_attrs = [] |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
619 changed = False |
849 | 620 |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
621 for name, value in attrs: |
483
5cc92db755c5
Fix for handling of interpolated attribute values in translation filter.
cmlenz
parents:
481
diff
changeset
|
622 newval = value |
790
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
623 if extract_text and isinstance(value, basestring): |
483
5cc92db755c5
Fix for handling of interpolated attribute values in translation filter.
cmlenz
parents:
481
diff
changeset
|
624 if name in include_attrs: |
788
31432f30a6fb
Change `Translator` class to accept either a `gettext`-style function, or an object compatible with the `NullTranslations` / `GNUTranslations` interface.
cmlenz
parents:
787
diff
changeset
|
625 newval = gettext(value) |
483
5cc92db755c5
Fix for handling of interpolated attribute values in translation filter.
cmlenz
parents:
481
diff
changeset
|
626 else: |
849 | 627 newval = list( |
628 self(_ensure(value), ctxt, search_text=False) | |
483
5cc92db755c5
Fix for handling of interpolated attribute values in translation filter.
cmlenz
parents:
481
diff
changeset
|
629 ) |
5cc92db755c5
Fix for handling of interpolated attribute values in translation filter.
cmlenz
parents:
481
diff
changeset
|
630 if newval != value: |
5cc92db755c5
Fix for handling of interpolated attribute values in translation filter.
cmlenz
parents:
481
diff
changeset
|
631 value = newval |
5cc92db755c5
Fix for handling of interpolated attribute values in translation filter.
cmlenz
parents:
481
diff
changeset
|
632 changed = True |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
633 new_attrs.append((name, value)) |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
634 if changed: |
667
c9a084ffaee6
Fix case where attributes weren't properly wrapped in an `Attrs` instance if one or more of them were translated by the I18n filter, potentially breaking things further down the chain. Closes #162.
cmlenz
parents:
600
diff
changeset
|
635 attrs = Attrs(new_attrs) |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
636 |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
637 yield kind, (tag, attrs), pos |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
638 |
485
fb66fb3e4b49
Follow-up to [583]: Don't extract strings from interpolated attribute values for attributes that shouldn't be included.
cmlenz
parents:
483
diff
changeset
|
639 elif search_text and kind is TEXT: |
790
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
640 text = data.strip() |
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
641 if text: |
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
642 data = data.replace(text, unicode(gettext(text))) |
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
643 yield kind, data, pos |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
644 |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
645 elif kind is SUB: |
790
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
646 directives, substream = data |
849 | 647 current_domain = None |
648 for idx, directive in enumerate(directives): | |
649 # Organize directives to make everything work | |
650 if isinstance(directive, DomainDirective): | |
651 # Grab current domain and update context | |
652 current_domain = directive.domain | |
653 ctxt.push({'_i18n.domain': current_domain}) | |
654 # Put domain directive as the first one in order to | |
655 # update context before any other directives evaluation | |
656 directives.insert(0, directives.pop(idx)) | |
657 | |
658 # If this is an i18n directive, no need to translate text | |
790
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
659 # nodes here |
854
4d9bef447df9
More work on reducing the size of the diff produced by 2to3.
cmlenz
parents:
853
diff
changeset
|
660 is_i18n_directive = [b for b in |
4d9bef447df9
More work on reducing the size of the diff produced by 2to3.
cmlenz
parents:
853
diff
changeset
|
661 [isinstance(d, ExtractableI18NDirective) |
4d9bef447df9
More work on reducing the size of the diff produced by 2to3.
cmlenz
parents:
853
diff
changeset
|
662 for d in directives] if b] |
790
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
663 substream = list(self(substream, ctxt, |
849 | 664 search_text=not is_i18n_directive)) |
790
da90cee22560
Merged the custom-directives branch back into trunk.
cmlenz
parents:
788
diff
changeset
|
665 yield kind, (directives, substream), pos |
560
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
666 |
849 | 667 if current_domain: |
668 ctxt.pop() | |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
669 else: |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
670 yield kind, data, pos |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
671 |
450
94601511cd68
Extend the I18n extraction to also yield function names if applicable.
cmlenz
parents:
448
diff
changeset
|
672 GETTEXT_FUNCTIONS = ('_', 'gettext', 'ngettext', 'dgettext', 'dngettext', |
94601511cd68
Extend the I18n extraction to also yield function names if applicable.
cmlenz
parents:
448
diff
changeset
|
673 'ugettext', 'ungettext') |
94601511cd68
Extend the I18n extraction to also yield function names if applicable.
cmlenz
parents:
448
diff
changeset
|
674 |
485
fb66fb3e4b49
Follow-up to [583]: Don't extract strings from interpolated attribute values for attributes that shouldn't be included.
cmlenz
parents:
483
diff
changeset
|
675 def extract(self, stream, gettext_functions=GETTEXT_FUNCTIONS, |
849 | 676 search_text=True, msgbuf=None, comment_stack=None): |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
677 """Extract localizable strings from the given template stream. |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
678 |
450
94601511cd68
Extend the I18n extraction to also yield function names if applicable.
cmlenz
parents:
448
diff
changeset
|
679 For every string found, this function yields a ``(lineno, function, |
787
d7366797440f
Add support for supplying comments on localizable messages in the i18n filter. Based on patch by Pedro Algarvio on #129.
cmlenz
parents:
785
diff
changeset
|
680 message, comments)`` tuple, where: |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
681 |
450
94601511cd68
Extend the I18n extraction to also yield function names if applicable.
cmlenz
parents:
448
diff
changeset
|
682 * ``lineno`` is the number of the line on which the string was found, |
94601511cd68
Extend the I18n extraction to also yield function names if applicable.
cmlenz
parents:
448
diff
changeset
|
683 * ``function`` is the name of the ``gettext`` function used (if the |
94601511cd68
Extend the I18n extraction to also yield function names if applicable.
cmlenz
parents:
448
diff
changeset
|
684 string was extracted from embedded Python code), and |
469
2d3246f9ea54
The I18n extraction now returns a tuple of strings for `ngettext` and similar functions.
cmlenz
parents:
467
diff
changeset
|
685 * ``message`` is the string itself (a ``unicode`` object, or a tuple |
787
d7366797440f
Add support for supplying comments on localizable messages in the i18n filter. Based on patch by Pedro Algarvio on #129.
cmlenz
parents:
785
diff
changeset
|
686 of ``unicode`` objects for functions with multiple string |
d7366797440f
Add support for supplying comments on localizable messages in the i18n filter. Based on patch by Pedro Algarvio on #129.
cmlenz
parents:
785
diff
changeset
|
687 arguments). |
d7366797440f
Add support for supplying comments on localizable messages in the i18n filter. Based on patch by Pedro Algarvio on #129.
cmlenz
parents:
785
diff
changeset
|
688 * ``comments`` is a list of comments related to the message, extracted |
d7366797440f
Add support for supplying comments on localizable messages in the i18n filter. Based on patch by Pedro Algarvio on #129.
cmlenz
parents:
785
diff
changeset
|
689 from ``i18n:comment`` attributes found in the markup |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
690 |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
691 >>> tmpl = MarkupTemplate('''<html xmlns:py="http://genshi.edgewall.org/"> |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
692 ... <head> |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
693 ... <title>Example</title> |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
694 ... </head> |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
695 ... <body> |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
696 ... <h1>Example</h1> |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
697 ... <p>${_("Hello, %(name)s") % dict(name=username)}</p> |
469
2d3246f9ea54
The I18n extraction now returns a tuple of strings for `ngettext` and similar functions.
cmlenz
parents:
467
diff
changeset
|
698 ... <p>${ngettext("You have %d item", "You have %d items", num)}</p> |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
699 ... </body> |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
700 ... </html>''', filename='example.html') |
787
d7366797440f
Add support for supplying comments on localizable messages in the i18n filter. Based on patch by Pedro Algarvio on #129.
cmlenz
parents:
785
diff
changeset
|
701 >>> for line, func, msg, comments in Translator().extract(tmpl.stream): |
853
f33ecf3c319e
Convert a bunch of print statements to py3k compatible syntax.
cmlenz
parents:
850
diff
changeset
|
702 ... print('%d, %r, %r' % (line, func, msg)) |
450
94601511cd68
Extend the I18n extraction to also yield function names if applicable.
cmlenz
parents:
448
diff
changeset
|
703 3, None, u'Example' |
94601511cd68
Extend the I18n extraction to also yield function names if applicable.
cmlenz
parents:
448
diff
changeset
|
704 6, None, u'Example' |
94601511cd68
Extend the I18n extraction to also yield function names if applicable.
cmlenz
parents:
448
diff
changeset
|
705 7, '_', u'Hello, %(name)s' |
565
53b37e4f2921
* The I18n extractor now handles gettext function calls that use non-string parameters as well as keyword arguments.
cmlenz
parents:
561
diff
changeset
|
706 8, 'ngettext', (u'You have %d item', u'You have %d items', None) |
469
2d3246f9ea54
The I18n extraction now returns a tuple of strings for `ngettext` and similar functions.
cmlenz
parents:
467
diff
changeset
|
707 |
450
94601511cd68
Extend the I18n extraction to also yield function names if applicable.
cmlenz
parents:
448
diff
changeset
|
708 :param stream: the event stream to extract strings from; can be a |
94601511cd68
Extend the I18n extraction to also yield function names if applicable.
cmlenz
parents:
448
diff
changeset
|
709 regular stream or a template stream |
94601511cd68
Extend the I18n extraction to also yield function names if applicable.
cmlenz
parents:
448
diff
changeset
|
710 :param gettext_functions: a sequence of function names that should be |
94601511cd68
Extend the I18n extraction to also yield function names if applicable.
cmlenz
parents:
448
diff
changeset
|
711 treated as gettext-style localization |
94601511cd68
Extend the I18n extraction to also yield function names if applicable.
cmlenz
parents:
448
diff
changeset
|
712 functions |
485
fb66fb3e4b49
Follow-up to [583]: Don't extract strings from interpolated attribute values for attributes that shouldn't be included.
cmlenz
parents:
483
diff
changeset
|
713 :param search_text: whether the content of text nodes should be |
fb66fb3e4b49
Follow-up to [583]: Don't extract strings from interpolated attribute values for attributes that shouldn't be included.
cmlenz
parents:
483
diff
changeset
|
714 extracted (used internally) |
469
2d3246f9ea54
The I18n extraction now returns a tuple of strings for `ngettext` and similar functions.
cmlenz
parents:
467
diff
changeset
|
715 |
2d3246f9ea54
The I18n extraction now returns a tuple of strings for `ngettext` and similar functions.
cmlenz
parents:
467
diff
changeset
|
716 :note: Changed in 0.4.1: For a function with multiple string arguments |
2d3246f9ea54
The I18n extraction now returns a tuple of strings for `ngettext` and similar functions.
cmlenz
parents:
467
diff
changeset
|
717 (such as ``ngettext``), a single item with a tuple of strings is |
2d3246f9ea54
The I18n extraction now returns a tuple of strings for `ngettext` and similar functions.
cmlenz
parents:
467
diff
changeset
|
718 yielded, instead an item for each string argument. |
849 | 719 :note: Changed in 0.6: The returned tuples now include a fourth |
720 element, which is a list of comments for the translator. | |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
721 """ |
594
2bbaa61b328f
Add option to I18n filter to only extract strings in gettext function calls.
cmlenz
parents:
576
diff
changeset
|
722 if not self.extract_text: |
2bbaa61b328f
Add option to I18n filter to only extract strings in gettext function calls.
cmlenz
parents:
576
diff
changeset
|
723 search_text = False |
849 | 724 if comment_stack is None: |
725 comment_stack = [] | |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
726 skip = 0 |
849 | 727 |
728 # Un-comment bellow to extract messages without adding directives | |
522
082535e5087c
The I18n filter now skips the content of elements that have an `xml:lang` attribute with a fixed string value. Basically, `xml:lang` can now be used as a flag to mark specific sections as not needing localization.
cmlenz
parents:
501
diff
changeset
|
729 xml_lang = XML_NAMESPACE['lang'] |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
730 |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
731 for kind, data, pos in stream: |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
732 if skip: |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
733 if kind is START: |
522
082535e5087c
The I18n filter now skips the content of elements that have an `xml:lang` attribute with a fixed string value. Basically, `xml:lang` can now be used as a flag to mark specific sections as not needing localization.
cmlenz
parents:
501
diff
changeset
|
734 skip += 1 |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
735 if kind is END: |
522
082535e5087c
The I18n filter now skips the content of elements that have an `xml:lang` attribute with a fixed string value. Basically, `xml:lang` can now be used as a flag to mark specific sections as not needing localization.
cmlenz
parents:
501
diff
changeset
|
736 skip -= 1 |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
737 |
549
7214c1bdb383
The I18n filter now extracts text from translation functions in ignored tags. Fixes #132.
cmlenz
parents:
535
diff
changeset
|
738 if kind is START and not skip: |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
739 tag, attrs = data |
560
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
740 |
522
082535e5087c
The I18n filter now skips the content of elements that have an `xml:lang` attribute with a fixed string value. Basically, `xml:lang` can now be used as a flag to mark specific sections as not needing localization.
cmlenz
parents:
501
diff
changeset
|
741 if tag in self.ignore_tags or \ |
082535e5087c
The I18n filter now skips the content of elements that have an `xml:lang` attribute with a fixed string value. Basically, `xml:lang` can now be used as a flag to mark specific sections as not needing localization.
cmlenz
parents:
501
diff
changeset
|
742 isinstance(attrs.get(xml_lang), basestring): |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
743 skip += 1 |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
744 continue |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
745 |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
746 for name, value in attrs: |
594
2bbaa61b328f
Add option to I18n filter to only extract strings in gettext function calls.
cmlenz
parents:
576
diff
changeset
|
747 if search_text and isinstance(value, basestring): |
483
5cc92db755c5
Fix for handling of interpolated attribute values in translation filter.
cmlenz
parents:
481
diff
changeset
|
748 if name in self.include_attrs: |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
749 text = value.strip() |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
750 if text: |
849 | 751 # XXX: Do we need to grab i18n:comment from comment_stack ??? |
787
d7366797440f
Add support for supplying comments on localizable messages in the i18n filter. Based on patch by Pedro Algarvio on #129.
cmlenz
parents:
785
diff
changeset
|
752 yield pos[1], None, text, [] |
483
5cc92db755c5
Fix for handling of interpolated attribute values in translation filter.
cmlenz
parents:
481
diff
changeset
|
753 else: |
787
d7366797440f
Add support for supplying comments on localizable messages in the i18n filter. Based on patch by Pedro Algarvio on #129.
cmlenz
parents:
785
diff
changeset
|
754 for lineno, funcname, text, comments in self.extract( |
485
fb66fb3e4b49
Follow-up to [583]: Don't extract strings from interpolated attribute values for attributes that shouldn't be included.
cmlenz
parents:
483
diff
changeset
|
755 _ensure(value), gettext_functions, |
535
35a413f3f1dd
The I18n filter no longer extracts or translates literal strings in attribute values that also contain expressions.
cmlenz
parents:
531
diff
changeset
|
756 search_text=False): |
787
d7366797440f
Add support for supplying comments on localizable messages in the i18n filter. Based on patch by Pedro Algarvio on #129.
cmlenz
parents:
785
diff
changeset
|
757 yield lineno, funcname, text, comments |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
758 |
561
21b473243e63
Move code for extracting messages from bytecode into a separate function.
cmlenz
parents:
560
diff
changeset
|
759 if msgbuf: |
21b473243e63
Move code for extracting messages from bytecode into a separate function.
cmlenz
parents:
560
diff
changeset
|
760 msgbuf.append(kind, data, pos) |
21b473243e63
Move code for extracting messages from bytecode into a separate function.
cmlenz
parents:
560
diff
changeset
|
761 |
549
7214c1bdb383
The I18n filter now extracts text from translation functions in ignored tags. Fixes #132.
cmlenz
parents:
535
diff
changeset
|
762 elif not skip and search_text and kind is TEXT: |
560
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
763 if not msgbuf: |
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
764 text = data.strip() |
854
4d9bef447df9
More work on reducing the size of the diff produced by 2to3.
cmlenz
parents:
853
diff
changeset
|
765 if text and [ch for ch in text if ch.isalpha()]: |
849 | 766 yield pos[1], None, text, comment_stack[-1:] |
560
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
767 else: |
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
768 msgbuf.append(kind, data, pos) |
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
769 |
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
770 elif not skip and msgbuf and kind is END: |
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
771 msgbuf.append(kind, data, pos) |
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
772 if not msgbuf.depth: |
854
4d9bef447df9
More work on reducing the size of the diff produced by 2to3.
cmlenz
parents:
853
diff
changeset
|
773 yield msgbuf.lineno, None, msgbuf.format(), [ |
4d9bef447df9
More work on reducing the size of the diff produced by 2to3.
cmlenz
parents:
853
diff
changeset
|
774 c for c in msgbuf.comment if c |
4d9bef447df9
More work on reducing the size of the diff produced by 2to3.
cmlenz
parents:
853
diff
changeset
|
775 ] |
560
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
776 msgbuf = None |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
777 |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
778 elif kind is EXPR or kind is EXEC: |
775
886934df7fea
Support for parameters in internationalized `i18n:msg` content. See #129.
cmlenz
parents:
762
diff
changeset
|
779 if msgbuf: |
886934df7fea
Support for parameters in internationalized `i18n:msg` content. See #129.
cmlenz
parents:
762
diff
changeset
|
780 msgbuf.append(kind, data, pos) |
565
53b37e4f2921
* The I18n extractor now handles gettext function calls that use non-string parameters as well as keyword arguments.
cmlenz
parents:
561
diff
changeset
|
781 for funcname, strings in extract_from_code(data, |
561
21b473243e63
Move code for extracting messages from bytecode into a separate function.
cmlenz
parents:
560
diff
changeset
|
782 gettext_functions): |
849 | 783 # XXX: Do we need to grab i18n:comment from comment_stack ??? |
787
d7366797440f
Add support for supplying comments on localizable messages in the i18n filter. Based on patch by Pedro Algarvio on #129.
cmlenz
parents:
785
diff
changeset
|
784 yield pos[1], funcname, strings, [] |
446
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
785 |
fd9c4f7a249a
Add basic I18n/L10n functionality, based on GenshiRecipes/Localization.
cmlenz
parents:
diff
changeset
|
786 elif kind is SUB: |
849 | 787 directives, substream = data |
788 in_comment = False | |
789 | |
790 for idx, directive in enumerate(directives): | |
791 # Do a first loop to see if there's a comment directive | |
792 # If there is update context and pop it from directives | |
793 if isinstance(directive, CommentDirective): | |
794 in_comment = True | |
795 comment_stack.append(directive.comment) | |
796 if len(directives) == 1: | |
797 # in case we're in the presence of something like: | |
798 # <p i18n:comment="foo">Foo</p> | |
799 messages = self.extract( | |
800 substream, gettext_functions, | |
801 search_text=search_text and not skip, | |
802 msgbuf=msgbuf, comment_stack=comment_stack) | |
803 for lineno, funcname, text, comments in messages: | |
804 yield lineno, funcname, text, comments | |
805 directives.pop(idx) | |
806 elif not isinstance(directive, I18NDirective): | |
807 # Remove all other non i18n directives from the process | |
808 directives.pop(idx) | |
809 | |
810 if not directives and not in_comment: | |
811 # Extract content if there's no directives because | |
812 # strip was pop'ed and not because comment was pop'ed. | |
813 # Extraction in this case has been taken care of. | |
814 messages = self.extract( | |
815 substream, gettext_functions, | |
816 search_text=search_text and not skip, msgbuf=msgbuf) | |
817 for lineno, funcname, text, comments in messages: | |
818 yield lineno, funcname, text, comments | |
819 | |
820 for directive in directives: | |
821 if isinstance(directive, ExtractableI18NDirective): | |
822 messages = directive.extract(substream, comment_stack) | |
823 for funcname, text, comments in messages: | |
824 yield pos[1], funcname, text, comments | |
825 else: | |
826 messages = self.extract( | |
827 substream, gettext_functions, | |
828 search_text=search_text and not skip, msgbuf=msgbuf) | |
829 for lineno, funcname, text, comments in messages: | |
830 yield lineno, funcname, text, comments | |
831 | |
832 if in_comment: | |
833 comment_stack.pop() | |
834 | |
835 def get_directive_index(self, dir_cls): | |
836 total = len(self._dir_order) | |
837 if dir_cls in self._dir_order: | |
838 return self._dir_order.index(dir_cls) - total | |
839 return total | |
840 | |
841 def setup(self, template): | |
842 """Convenience function to register the `Translator` filter and the | |
843 related directives with the given template. | |
844 | |
845 :param template: a `Template` instance | |
846 """ | |
847 template.filters.insert(0, self) | |
848 if hasattr(template, 'add_directives'): | |
849 template.add_directives(Translator.NAMESPACE, self) | |
528
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
850 |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
851 |
560
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
852 class MessageBuffer(object): |
738 | 853 """Helper class for managing internationalized mixed content. |
576 | 854 |
855 :since: version 0.5 | |
856 """ | |
560
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
857 |
849 | 858 def __init__(self, directive=None): |
738 | 859 """Initialize the message buffer. |
860 | |
776
ddb58d74c8ee
Added tests for the parameter support added to advanced internationalization in [901]. See #129.
cmlenz
parents:
775
diff
changeset
|
861 :param params: comma-separated list of parameter names |
ddb58d74c8ee
Added tests for the parameter support added to advanced internationalization in [901]. See #129.
cmlenz
parents:
775
diff
changeset
|
862 :type params: `basestring` |
738 | 863 :param lineno: the line number on which the first stream event |
864 belonging to the message was found | |
865 """ | |
849 | 866 # params list needs to be copied so that directives can be evaluated |
867 # more than once | |
868 self.orig_params = self.params = directive.params[:] | |
869 self.directive = directive | |
738 | 870 self.string = [] |
560
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
871 self.events = {} |
775
886934df7fea
Support for parameters in internationalized `i18n:msg` content. See #129.
cmlenz
parents:
762
diff
changeset
|
872 self.values = {} |
560
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
873 self.depth = 1 |
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
874 self.order = 1 |
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
875 self.stack = [0] |
849 | 876 self.subdirectives = {} |
560
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
877 |
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
878 def append(self, kind, data, pos): |
738 | 879 """Append a stream event to the buffer. |
880 | |
881 :param kind: the stream event kind | |
882 :param data: the event data | |
883 :param pos: the position of the event in the source | |
884 """ | |
849 | 885 if kind is SUB: |
886 # The order needs to be +1 because a new START kind event will | |
887 # happen and we we need to wrap those events into our custom kind(s) | |
888 order = self.stack[-1] + 1 | |
889 subdirectives, substream = data | |
890 # Store the directives that should be applied after translation | |
891 self.subdirectives.setdefault(order, []).extend(subdirectives) | |
892 self.events.setdefault(order, []).append((SUB_START, None, pos)) | |
893 for skind, sdata, spos in substream: | |
894 self.append(skind, sdata, spos) | |
895 self.events.setdefault(order, []).append((SUB_END, None, pos)) | |
896 elif kind is TEXT: | |
897 if '[' in data or ']' in data: | |
898 # Quote [ and ] if it ain't us adding it, ie, if the user is | |
899 # using those chars in his templates, escape them | |
900 data = data.replace('[', '\[').replace(']', '\]') | |
738 | 901 self.string.append(data) |
849 | 902 self.events.setdefault(self.stack[-1], []).append((kind, data, pos)) |
775
886934df7fea
Support for parameters in internationalized `i18n:msg` content. See #129.
cmlenz
parents:
762
diff
changeset
|
903 elif kind is EXPR: |
849 | 904 if self.params: |
905 param = self.params.pop(0) | |
906 else: | |
907 params = ', '.join(['"%s"' % p for p in self.orig_params if p]) | |
908 if params: | |
909 params = "(%s)" % params | |
910 raise IndexError("%d parameters%s given to 'i18n:%s' but " | |
911 "%d or more expressions used in '%s', line %s" | |
912 % (len(self.orig_params), params, | |
913 self.directive.tagname, | |
914 len(self.orig_params)+1, | |
915 os.path.basename(pos[0] or | |
916 'In Memmory Template'), | |
917 pos[1])) | |
775
886934df7fea
Support for parameters in internationalized `i18n:msg` content. See #129.
cmlenz
parents:
762
diff
changeset
|
918 self.string.append('%%(%s)s' % param) |
849 | 919 self.events.setdefault(self.stack[-1], []).append((kind, data, pos)) |
775
886934df7fea
Support for parameters in internationalized `i18n:msg` content. See #129.
cmlenz
parents:
762
diff
changeset
|
920 self.values[param] = (kind, data, pos) |
560
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
921 else: |
849 | 922 if kind is START: |
854
4d9bef447df9
More work on reducing the size of the diff produced by 2to3.
cmlenz
parents:
853
diff
changeset
|
923 self.string.append('[%d:' % self.order) |
560
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
924 self.stack.append(self.order) |
849 | 925 self.events.setdefault(self.stack[-1], |
926 []).append((kind, data, pos)) | |
560
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
927 self.depth += 1 |
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
928 self.order += 1 |
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
929 elif kind is END: |
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
930 self.depth -= 1 |
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
931 if self.depth: |
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
932 self.events[self.stack[-1]].append((kind, data, pos)) |
854
4d9bef447df9
More work on reducing the size of the diff produced by 2to3.
cmlenz
parents:
853
diff
changeset
|
933 self.string.append(']') |
560
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
934 self.stack.pop() |
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
935 |
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
936 def format(self): |
738 | 937 """Return a message identifier representing the content in the |
938 buffer. | |
939 """ | |
854
4d9bef447df9
More work on reducing the size of the diff produced by 2to3.
cmlenz
parents:
853
diff
changeset
|
940 return ''.join(self.string).strip() |
560
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
941 |
775
886934df7fea
Support for parameters in internationalized `i18n:msg` content. See #129.
cmlenz
parents:
762
diff
changeset
|
942 def translate(self, string, regex=re.compile(r'%\((\w+)\)s')): |
738 | 943 """Interpolate the given message translation with the events in the |
944 buffer and return the translated stream. | |
945 | |
946 :param string: the translated message string | |
947 """ | |
849 | 948 substream = None |
949 | |
950 def yield_parts(string): | |
951 for idx, part in enumerate(regex.split(string)): | |
952 if idx % 2: | |
953 yield self.values[part] | |
954 elif part: | |
955 yield (TEXT, | |
956 part.replace('\[', '[').replace('\]', ']'), | |
957 (None, -1, -1) | |
958 ) | |
959 | |
560
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
960 parts = parse_msg(string) |
849 | 961 parts_counter = {} |
560
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
962 for order, string in parts: |
849 | 963 parts_counter.setdefault(order, []).append(None) |
964 | |
965 while parts: | |
966 order, string = parts.pop(0) | |
967 if len(parts_counter[order]) == 1: | |
968 events = self.events[order] | |
969 else: | |
970 events = [self.events[order].pop(0)] | |
971 parts_counter[order].pop() | |
972 | |
973 for event in events: | |
974 if event[0] is SUB_START: | |
975 substream = [] | |
976 elif event[0] is SUB_END: | |
977 # Yield a substream which might have directives to be | |
978 # applied to it (after translation events) | |
979 yield SUB, (self.subdirectives[order], substream), event[2] | |
980 substream = None | |
981 elif event[0] is TEXT: | |
982 if string: | |
983 for part in yield_parts(string): | |
984 if substream is not None: | |
985 substream.append(part) | |
986 else: | |
987 yield part | |
988 # String handled, reset it | |
989 string = None | |
990 elif event[0] is START: | |
991 if substream is not None: | |
992 substream.append(event) | |
993 else: | |
994 yield event | |
995 if string: | |
996 for part in yield_parts(string): | |
997 if substream is not None: | |
998 substream.append(part) | |
999 else: | |
1000 yield part | |
1001 # String handled, reset it | |
1002 string = None | |
1003 elif event[0] is END: | |
1004 if string: | |
1005 for part in yield_parts(string): | |
1006 if substream is not None: | |
1007 substream.append(part) | |
1008 else: | |
1009 yield part | |
1010 # String handled, reset it | |
1011 string = None | |
1012 if substream is not None: | |
1013 substream.append(event) | |
1014 else: | |
1015 yield event | |
1016 elif event[0] is EXPR: | |
1017 # These are handled on the strings itself | |
1018 continue | |
775
886934df7fea
Support for parameters in internationalized `i18n:msg` content. See #129.
cmlenz
parents:
762
diff
changeset
|
1019 else: |
849 | 1020 if string: |
1021 for part in yield_parts(string): | |
1022 if substream is not None: | |
1023 substream.append(part) | |
1024 else: | |
1025 yield part | |
1026 # String handled, reset it | |
1027 string = None | |
1028 if substream is not None: | |
1029 substream.append(event) | |
1030 else: | |
1031 yield event | |
560
7e83be231f96
Start implementation of advanced I18n as dicussed in #129 and the MailingList. This is not complete yet, but many simple cases work okay.
cmlenz
parents:
549
diff
changeset
|
1032 |
849 | 1033 def parse_msg(string, regex=re.compile(r'(?:\[(\d+)\:)|(?<!\\)\]')): |
738 | 1034 """Parse a translated message using Genshi mixed content message |
1035 formatting. | |
849 | 1036 |
738 | 1037 >>> parse_msg("See [1:Help].") |
1038 [(0, 'See '), (1, 'Help'), (0, '.')] | |
849 | 1039 |
738 | 1040 >>> parse_msg("See [1:our [2:Help] page] for details.") |
1041 [(0, 'See '), (1, 'our '), (2, 'Help'), (1, ' page'), (0, ' for details.')] | |
849 | 1042 |
738 | 1043 >>> parse_msg("[2:Details] finden Sie in [1:Hilfe].") |
1044 [(2, 'Details'), (0, ' finden Sie in '), (1, 'Hilfe'), (0, '.')] | |
849 | 1045 |
738 | 1046 >>> parse_msg("[1:] Bilder pro Seite anzeigen.") |
1047 [(1, ''), (0, ' Bilder pro Seite anzeigen.')] | |
849 | 1048 |
738 | 1049 :param string: the translated message string |
1050 :return: a list of ``(order, string)`` tuples | |
1051 :rtype: `list` | |
1052 """ | |
1053 parts = [] | |
1054 stack = [0] | |
1055 while True: | |
1056 mo = regex.search(string) | |
1057 if not mo: | |
1058 break | |
1059 | |
1060 if mo.start() or stack[-1]: | |
1061 parts.append((stack[-1], string[:mo.start()])) | |
1062 string = string[mo.end():] | |
1063 | |
1064 orderno = mo.group(1) | |
1065 if orderno is not None: | |
1066 stack.append(int(orderno)) | |
1067 else: | |
1068 stack.pop() | |
1069 if not stack: | |
1070 break | |
1071 | |
1072 if string: | |
1073 parts.append((stack[-1], string)) | |
1074 | |
1075 return parts | |
1076 | |
776
ddb58d74c8ee
Added tests for the parameter support added to advanced internationalization in [901]. See #129.
cmlenz
parents:
775
diff
changeset
|
1077 |
561
21b473243e63
Move code for extracting messages from bytecode into a separate function.
cmlenz
parents:
560
diff
changeset
|
1078 def extract_from_code(code, gettext_functions): |
21b473243e63
Move code for extracting messages from bytecode into a separate function.
cmlenz
parents:
560
diff
changeset
|
1079 """Extract strings from Python bytecode. |
21b473243e63
Move code for extracting messages from bytecode into a separate function.
cmlenz
parents:
560
diff
changeset
|
1080 |
21b473243e63
Move code for extracting messages from bytecode into a separate function.
cmlenz
parents:
560
diff
changeset
|
1081 >>> from genshi.template.eval import Expression |
21b473243e63
Move code for extracting messages from bytecode into a separate function.
cmlenz
parents:
560
diff
changeset
|
1082 >>> expr = Expression('_("Hello")') |
565
53b37e4f2921
* The I18n extractor now handles gettext function calls that use non-string parameters as well as keyword arguments.
cmlenz
parents:
561
diff
changeset
|
1083 >>> list(extract_from_code(expr, Translator.GETTEXT_FUNCTIONS)) |
561
21b473243e63
Move code for extracting messages from bytecode into a separate function.
cmlenz
parents:
560
diff
changeset
|
1084 [('_', u'Hello')] |
849 | 1085 |
561
21b473243e63
Move code for extracting messages from bytecode into a separate function.
cmlenz
parents:
560
diff
changeset
|
1086 >>> expr = Expression('ngettext("You have %(num)s item", ' |
21b473243e63
Move code for extracting messages from bytecode into a separate function.
cmlenz
parents:
560
diff
changeset
|
1087 ... '"You have %(num)s items", num)') |
565
53b37e4f2921
* The I18n extractor now handles gettext function calls that use non-string parameters as well as keyword arguments.
cmlenz
parents:
561
diff
changeset
|
1088 >>> list(extract_from_code(expr, Translator.GETTEXT_FUNCTIONS)) |
53b37e4f2921
* The I18n extractor now handles gettext function calls that use non-string parameters as well as keyword arguments.
cmlenz
parents:
561
diff
changeset
|
1089 [('ngettext', (u'You have %(num)s item', u'You have %(num)s items', None))] |
561
21b473243e63
Move code for extracting messages from bytecode into a separate function.
cmlenz
parents:
560
diff
changeset
|
1090 |
565
53b37e4f2921
* The I18n extractor now handles gettext function calls that use non-string parameters as well as keyword arguments.
cmlenz
parents:
561
diff
changeset
|
1091 :param code: the `Code` object |
53b37e4f2921
* The I18n extractor now handles gettext function calls that use non-string parameters as well as keyword arguments.
cmlenz
parents:
561
diff
changeset
|
1092 :type code: `genshi.template.eval.Code` |
561
21b473243e63
Move code for extracting messages from bytecode into a separate function.
cmlenz
parents:
560
diff
changeset
|
1093 :param gettext_functions: a sequence of function names |
576 | 1094 :since: version 0.5 |
561
21b473243e63
Move code for extracting messages from bytecode into a separate function.
cmlenz
parents:
560
diff
changeset
|
1095 """ |
565
53b37e4f2921
* The I18n extractor now handles gettext function calls that use non-string parameters as well as keyword arguments.
cmlenz
parents:
561
diff
changeset
|
1096 def _walk(node): |
794
f9e23d472a6e
Merged AST branch back into trunk. Most of this code was written by Marcin Kurczych for his Google Summer of Code 2008 project. The merge of this branch means that Genshi now uses the native `_ast` module on Python >= 2.5, and an emulation thereof on Python 2.4. This replaces the usage of the `compiler` package, which was deprecated in Python 2.6 and removed in Python 3.0. Another effect is that Genshi now runs on Google AppEngine (although performance is bad due to the lack of template caching).
cmlenz
parents:
790
diff
changeset
|
1097 if isinstance(node, _ast.Call) and isinstance(node.func, _ast.Name) \ |
f9e23d472a6e
Merged AST branch back into trunk. Most of this code was written by Marcin Kurczych for his Google Summer of Code 2008 project. The merge of this branch means that Genshi now uses the native `_ast` module on Python >= 2.5, and an emulation thereof on Python 2.4. This replaces the usage of the `compiler` package, which was deprecated in Python 2.6 and removed in Python 3.0. Another effect is that Genshi now runs on Google AppEngine (although performance is bad due to the lack of template caching).
cmlenz
parents:
790
diff
changeset
|
1098 and node.func.id in gettext_functions: |
565
53b37e4f2921
* The I18n extractor now handles gettext function calls that use non-string parameters as well as keyword arguments.
cmlenz
parents:
561
diff
changeset
|
1099 strings = [] |
600
23a4784203ae
Handle starargs and dstarargs in the I18n extraction code.
cmlenz
parents:
596
diff
changeset
|
1100 def _add(arg): |
794
f9e23d472a6e
Merged AST branch back into trunk. Most of this code was written by Marcin Kurczych for his Google Summer of Code 2008 project. The merge of this branch means that Genshi now uses the native `_ast` module on Python >= 2.5, and an emulation thereof on Python 2.4. This replaces the usage of the `compiler` package, which was deprecated in Python 2.6 and removed in Python 3.0. Another effect is that Genshi now runs on Google AppEngine (although performance is bad due to the lack of template caching).
cmlenz
parents:
790
diff
changeset
|
1101 if isinstance(arg, _ast.Str) and isinstance(arg.s, basestring): |
f9e23d472a6e
Merged AST branch back into trunk. Most of this code was written by Marcin Kurczych for his Google Summer of Code 2008 project. The merge of this branch means that Genshi now uses the native `_ast` module on Python >= 2.5, and an emulation thereof on Python 2.4. This replaces the usage of the `compiler` package, which was deprecated in Python 2.6 and removed in Python 3.0. Another effect is that Genshi now runs on Google AppEngine (although performance is bad due to the lack of template caching).
cmlenz
parents:
790
diff
changeset
|
1102 strings.append(unicode(arg.s, 'utf-8')) |
f9e23d472a6e
Merged AST branch back into trunk. Most of this code was written by Marcin Kurczych for his Google Summer of Code 2008 project. The merge of this branch means that Genshi now uses the native `_ast` module on Python >= 2.5, and an emulation thereof on Python 2.4. This replaces the usage of the `compiler` package, which was deprecated in Python 2.6 and removed in Python 3.0. Another effect is that Genshi now runs on Google AppEngine (although performance is bad due to the lack of template caching).
cmlenz
parents:
790
diff
changeset
|
1103 elif arg: |
565
53b37e4f2921
* The I18n extractor now handles gettext function calls that use non-string parameters as well as keyword arguments.
cmlenz
parents:
561
diff
changeset
|
1104 strings.append(None) |
600
23a4784203ae
Handle starargs and dstarargs in the I18n extraction code.
cmlenz
parents:
596
diff
changeset
|
1105 [_add(arg) for arg in node.args] |
794
f9e23d472a6e
Merged AST branch back into trunk. Most of this code was written by Marcin Kurczych for his Google Summer of Code 2008 project. The merge of this branch means that Genshi now uses the native `_ast` module on Python >= 2.5, and an emulation thereof on Python 2.4. This replaces the usage of the `compiler` package, which was deprecated in Python 2.6 and removed in Python 3.0. Another effect is that Genshi now runs on Google AppEngine (although performance is bad due to the lack of template caching).
cmlenz
parents:
790
diff
changeset
|
1106 _add(node.starargs) |
f9e23d472a6e
Merged AST branch back into trunk. Most of this code was written by Marcin Kurczych for his Google Summer of Code 2008 project. The merge of this branch means that Genshi now uses the native `_ast` module on Python >= 2.5, and an emulation thereof on Python 2.4. This replaces the usage of the `compiler` package, which was deprecated in Python 2.6 and removed in Python 3.0. Another effect is that Genshi now runs on Google AppEngine (although performance is bad due to the lack of template caching).
cmlenz
parents:
790
diff
changeset
|
1107 _add(node.kwargs) |
565
53b37e4f2921
* The I18n extractor now handles gettext function calls that use non-string parameters as well as keyword arguments.
cmlenz
parents:
561
diff
changeset
|
1108 if len(strings) == 1: |
53b37e4f2921
* The I18n extractor now handles gettext function calls that use non-string parameters as well as keyword arguments.
cmlenz
parents:
561
diff
changeset
|
1109 strings = strings[0] |
561
21b473243e63
Move code for extracting messages from bytecode into a separate function.
cmlenz
parents:
560
diff
changeset
|
1110 else: |
565
53b37e4f2921
* The I18n extractor now handles gettext function calls that use non-string parameters as well as keyword arguments.
cmlenz
parents:
561
diff
changeset
|
1111 strings = tuple(strings) |
794
f9e23d472a6e
Merged AST branch back into trunk. Most of this code was written by Marcin Kurczych for his Google Summer of Code 2008 project. The merge of this branch means that Genshi now uses the native `_ast` module on Python >= 2.5, and an emulation thereof on Python 2.4. This replaces the usage of the `compiler` package, which was deprecated in Python 2.6 and removed in Python 3.0. Another effect is that Genshi now runs on Google AppEngine (although performance is bad due to the lack of template caching).
cmlenz
parents:
790
diff
changeset
|
1112 yield node.func.id, strings |
f9e23d472a6e
Merged AST branch back into trunk. Most of this code was written by Marcin Kurczych for his Google Summer of Code 2008 project. The merge of this branch means that Genshi now uses the native `_ast` module on Python >= 2.5, and an emulation thereof on Python 2.4. This replaces the usage of the `compiler` package, which was deprecated in Python 2.6 and removed in Python 3.0. Another effect is that Genshi now runs on Google AppEngine (although performance is bad due to the lack of template caching).
cmlenz
parents:
790
diff
changeset
|
1113 elif node._fields: |
f9e23d472a6e
Merged AST branch back into trunk. Most of this code was written by Marcin Kurczych for his Google Summer of Code 2008 project. The merge of this branch means that Genshi now uses the native `_ast` module on Python >= 2.5, and an emulation thereof on Python 2.4. This replaces the usage of the `compiler` package, which was deprecated in Python 2.6 and removed in Python 3.0. Another effect is that Genshi now runs on Google AppEngine (although performance is bad due to the lack of template caching).
cmlenz
parents:
790
diff
changeset
|
1114 children = [] |
f9e23d472a6e
Merged AST branch back into trunk. Most of this code was written by Marcin Kurczych for his Google Summer of Code 2008 project. The merge of this branch means that Genshi now uses the native `_ast` module on Python >= 2.5, and an emulation thereof on Python 2.4. This replaces the usage of the `compiler` package, which was deprecated in Python 2.6 and removed in Python 3.0. Another effect is that Genshi now runs on Google AppEngine (although performance is bad due to the lack of template caching).
cmlenz
parents:
790
diff
changeset
|
1115 for field in node._fields: |
f9e23d472a6e
Merged AST branch back into trunk. Most of this code was written by Marcin Kurczych for his Google Summer of Code 2008 project. The merge of this branch means that Genshi now uses the native `_ast` module on Python >= 2.5, and an emulation thereof on Python 2.4. This replaces the usage of the `compiler` package, which was deprecated in Python 2.6 and removed in Python 3.0. Another effect is that Genshi now runs on Google AppEngine (although performance is bad due to the lack of template caching).
cmlenz
parents:
790
diff
changeset
|
1116 child = getattr(node, field, None) |
f9e23d472a6e
Merged AST branch back into trunk. Most of this code was written by Marcin Kurczych for his Google Summer of Code 2008 project. The merge of this branch means that Genshi now uses the native `_ast` module on Python >= 2.5, and an emulation thereof on Python 2.4. This replaces the usage of the `compiler` package, which was deprecated in Python 2.6 and removed in Python 3.0. Another effect is that Genshi now runs on Google AppEngine (although performance is bad due to the lack of template caching).
cmlenz
parents:
790
diff
changeset
|
1117 if isinstance(child, list): |
f9e23d472a6e
Merged AST branch back into trunk. Most of this code was written by Marcin Kurczych for his Google Summer of Code 2008 project. The merge of this branch means that Genshi now uses the native `_ast` module on Python >= 2.5, and an emulation thereof on Python 2.4. This replaces the usage of the `compiler` package, which was deprecated in Python 2.6 and removed in Python 3.0. Another effect is that Genshi now runs on Google AppEngine (although performance is bad due to the lack of template caching).
cmlenz
parents:
790
diff
changeset
|
1118 for elem in child: |
f9e23d472a6e
Merged AST branch back into trunk. Most of this code was written by Marcin Kurczych for his Google Summer of Code 2008 project. The merge of this branch means that Genshi now uses the native `_ast` module on Python >= 2.5, and an emulation thereof on Python 2.4. This replaces the usage of the `compiler` package, which was deprecated in Python 2.6 and removed in Python 3.0. Another effect is that Genshi now runs on Google AppEngine (although performance is bad due to the lack of template caching).
cmlenz
parents:
790
diff
changeset
|
1119 children.append(elem) |
f9e23d472a6e
Merged AST branch back into trunk. Most of this code was written by Marcin Kurczych for his Google Summer of Code 2008 project. The merge of this branch means that Genshi now uses the native `_ast` module on Python >= 2.5, and an emulation thereof on Python 2.4. This replaces the usage of the `compiler` package, which was deprecated in Python 2.6 and removed in Python 3.0. Another effect is that Genshi now runs on Google AppEngine (although performance is bad due to the lack of template caching).
cmlenz
parents:
790
diff
changeset
|
1120 elif isinstance(child, _ast.AST): |
f9e23d472a6e
Merged AST branch back into trunk. Most of this code was written by Marcin Kurczych for his Google Summer of Code 2008 project. The merge of this branch means that Genshi now uses the native `_ast` module on Python >= 2.5, and an emulation thereof on Python 2.4. This replaces the usage of the `compiler` package, which was deprecated in Python 2.6 and removed in Python 3.0. Another effect is that Genshi now runs on Google AppEngine (although performance is bad due to the lack of template caching).
cmlenz
parents:
790
diff
changeset
|
1121 children.append(child) |
f9e23d472a6e
Merged AST branch back into trunk. Most of this code was written by Marcin Kurczych for his Google Summer of Code 2008 project. The merge of this branch means that Genshi now uses the native `_ast` module on Python >= 2.5, and an emulation thereof on Python 2.4. This replaces the usage of the `compiler` package, which was deprecated in Python 2.6 and removed in Python 3.0. Another effect is that Genshi now runs on Google AppEngine (although performance is bad due to the lack of template caching).
cmlenz
parents:
790
diff
changeset
|
1122 for child in children: |
565
53b37e4f2921
* The I18n extractor now handles gettext function calls that use non-string parameters as well as keyword arguments.
cmlenz
parents:
561
diff
changeset
|
1123 for funcname, strings in _walk(child): |
53b37e4f2921
* The I18n extractor now handles gettext function calls that use non-string parameters as well as keyword arguments.
cmlenz
parents:
561
diff
changeset
|
1124 yield funcname, strings |
53b37e4f2921
* The I18n extractor now handles gettext function calls that use non-string parameters as well as keyword arguments.
cmlenz
parents:
561
diff
changeset
|
1125 return _walk(code.ast) |
561
21b473243e63
Move code for extracting messages from bytecode into a separate function.
cmlenz
parents:
560
diff
changeset
|
1126 |
776
ddb58d74c8ee
Added tests for the parameter support added to advanced internationalization in [901]. See #129.
cmlenz
parents:
775
diff
changeset
|
1127 |
528
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1128 def extract(fileobj, keywords, comment_tags, options): |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1129 """Babel extraction method for Genshi templates. |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1130 |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1131 :param fileobj: the file-like object the messages should be extracted from |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1132 :param keywords: a list of keywords (i.e. function names) that should be |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1133 recognized as translation functions |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1134 :param comment_tags: a list of translator tags to search for and include |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1135 in the results |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1136 :param options: a dictionary of additional options (optional) |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1137 :return: an iterator over ``(lineno, funcname, message, comments)`` tuples |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1138 :rtype: ``iterator`` |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1139 """ |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1140 template_class = options.get('template_class', MarkupTemplate) |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1141 if isinstance(template_class, basestring): |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1142 module, clsname = template_class.split(':', 1) |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1143 template_class = getattr(__import__(module, {}, {}, [clsname]), clsname) |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1144 encoding = options.get('encoding', None) |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1145 |
596
badb5e5b7bb9
Follow-up to [708]. The added `extract_text` option wasn't actually being handled by the Babel extraction plugin.
cmlenz
parents:
594
diff
changeset
|
1146 extract_text = options.get('extract_text', True) |
badb5e5b7bb9
Follow-up to [708]. The added `extract_text` option wasn't actually being handled by the Babel extraction plugin.
cmlenz
parents:
594
diff
changeset
|
1147 if isinstance(extract_text, basestring): |
badb5e5b7bb9
Follow-up to [708]. The added `extract_text` option wasn't actually being handled by the Babel extraction plugin.
cmlenz
parents:
594
diff
changeset
|
1148 extract_text = extract_text.lower() in ('1', 'on', 'yes', 'true') |
badb5e5b7bb9
Follow-up to [708]. The added `extract_text` option wasn't actually being handled by the Babel extraction plugin.
cmlenz
parents:
594
diff
changeset
|
1149 |
528
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1150 ignore_tags = options.get('ignore_tags', Translator.IGNORE_TAGS) |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1151 if isinstance(ignore_tags, basestring): |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1152 ignore_tags = ignore_tags.split() |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1153 ignore_tags = [QName(tag) for tag in ignore_tags] |
596
badb5e5b7bb9
Follow-up to [708]. The added `extract_text` option wasn't actually being handled by the Babel extraction plugin.
cmlenz
parents:
594
diff
changeset
|
1154 |
528
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1155 include_attrs = options.get('include_attrs', Translator.INCLUDE_ATTRS) |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1156 if isinstance(include_attrs, basestring): |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1157 include_attrs = include_attrs.split() |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1158 include_attrs = [QName(attr) for attr in include_attrs] |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1159 |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1160 tmpl = template_class(fileobj, filename=getattr(fileobj, 'name', None), |
24df908da22d
Integrated [http://babel.edgewall.org/ Babel] message extraction plugin, and added I18n doc page.
cmlenz
parents:
522
diff
changeset
|
1161 encoding=encoding) |
849 | 1162 |
596
badb5e5b7bb9
Follow-up to [708]. The added `extract_text` option wasn't actually being handled by the Babel extraction plugin.
cmlenz
parents:
594
diff
changeset
|
1163 translator = Translator(None, ignore_tags, include_attrs, extract_text) |
849 | 1164 if hasattr(tmpl, 'add_directives'): |
1165 tmpl.add_directives(Translator.NAMESPACE, translator) | |
787
d7366797440f
Add support for supplying comments on localizable messages in the i18n filter. Based on patch by Pedro Algarvio on #129.
cmlenz
parents:
785
diff
changeset
|
1166 for message in translator.extract(tmpl.stream, gettext_functions=keywords): |
d7366797440f
Add support for supplying comments on localizable messages in the i18n filter. Based on patch by Pedro Algarvio on #129.
cmlenz
parents:
785
diff
changeset
|
1167 yield message |