Edgewall Software

source: trunk/genshi/filters/tests/i18n.py

Last change on this file was 1242, checked in by hodgestar, 10 years ago

Fix handling of case where a translation has text after a closing tag (fixes #566, thanks to jomae for the patch).

  • Property svn:eol-style set to native
File size: 87.5 KB
Line 
1# -*- coding: utf-8 -*-
2#
3# Copyright (C) 2007-2010 Edgewall Software
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
14from datetime import datetime
15import doctest
16from gettext import NullTranslations
17import unittest
18
19from genshi.core import Attrs
20from genshi.template import MarkupTemplate, Context
21from genshi.filters.i18n import Translator, extract
22from genshi.input import HTML
23from genshi.compat import IS_PYTHON2, StringIO
24
25
26class DummyTranslations(NullTranslations):
27    _domains = {}
28
29    def __init__(self, catalog=()):
30        NullTranslations.__init__(self)
31        self._catalog = catalog or {}
32        self.plural = lambda n: n != 1
33
34    def add_domain(self, domain, catalog):
35        translation = DummyTranslations(catalog)
36        translation.add_fallback(self)
37        self._domains[domain] = translation
38
39    def _domain_call(self, func, domain, *args, **kwargs):
40        return getattr(self._domains.get(domain, self), func)(*args, **kwargs)
41
42    if IS_PYTHON2:
43        def ugettext(self, message):
44            missing = object()
45            tmsg = self._catalog.get(message, missing)
46            if tmsg is missing:
47                if self._fallback:
48                    return self._fallback.ugettext(message)
49                return unicode(message)
50            return tmsg
51    else:
52        def gettext(self, message):
53            missing = object()
54            tmsg = self._catalog.get(message, missing)
55            if tmsg is missing:
56                if self._fallback:
57                    return self._fallback.gettext(message)
58                return unicode(message)
59            return tmsg
60
61    if IS_PYTHON2:
62        def dugettext(self, domain, message):
63            return self._domain_call('ugettext', domain, message)
64    else:
65        def dgettext(self, domain, message):
66            return self._domain_call('gettext', domain, message)
67
68    def ungettext(self, msgid1, msgid2, n):
69        try:
70            return self._catalog[(msgid1, self.plural(n))]
71        except KeyError:
72            if self._fallback:
73                return self._fallback.ngettext(msgid1, msgid2, n)
74            if n == 1:
75                return msgid1
76            else:
77                return msgid2
78
79    if not IS_PYTHON2:
80        ngettext = ungettext
81        del ungettext
82
83    if IS_PYTHON2:
84        def dungettext(self, domain, singular, plural, numeral):
85            return self._domain_call('ungettext', domain, singular, plural, numeral)
86    else:
87        def dngettext(self, domain, singular, plural, numeral):
88            return self._domain_call('ngettext', domain, singular, plural, numeral)
89
90
91class TranslatorTestCase(unittest.TestCase):
92
93    def test_translate_included_attribute_text(self):
94        """
95        Verify that translated attributes end up in a proper `Attrs` instance.
96        """
97        html = HTML(u"""<html>
98          <span title="Foo"></span>
99        </html>""")
100        translator = Translator(lambda s: u"Voh")
101        stream = list(html.filter(translator))
102        kind, data, pos = stream[2]
103        assert isinstance(data[1], Attrs)
104
105    def test_extract_without_text(self):
106        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
107          <p title="Bar">Foo</p>
108          ${ngettext("Singular", "Plural", num)}
109        </html>""")
110        translator = Translator(extract_text=False)
111        messages = list(translator.extract(tmpl.stream))
112        self.assertEqual(1, len(messages))
113        self.assertEqual((3, 'ngettext', ('Singular', 'Plural', None), []),
114                         messages[0])
115
116    def test_extract_plural_form(self):
117        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
118          ${ngettext("Singular", "Plural", num)}
119        </html>""")
120        translator = Translator()
121        messages = list(translator.extract(tmpl.stream))
122        self.assertEqual(1, len(messages))
123        self.assertEqual((2, 'ngettext', ('Singular', 'Plural', None), []),
124                         messages[0])
125
126    def test_extract_funky_plural_form(self):
127        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
128          ${ngettext(len(items), *widget.display_names)}
129        </html>""")
130        translator = Translator()
131        messages = list(translator.extract(tmpl.stream))
132        self.assertEqual(1, len(messages))
133        self.assertEqual((2, 'ngettext', (None, None), []), messages[0])
134
135    def test_extract_gettext_with_unicode_string(self):
136        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
137          ${gettext("Grüße")}
138        </html>""")
139        translator = Translator()
140        messages = list(translator.extract(tmpl.stream))
141        self.assertEqual(1, len(messages))
142        self.assertEqual((2, 'gettext', u'Gr\xfc\xdfe', []), messages[0])
143
144    def test_extract_included_attribute_text(self):
145        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
146          <span title="Foo"></span>
147        </html>""")
148        translator = Translator()
149        messages = list(translator.extract(tmpl.stream))
150        self.assertEqual(1, len(messages))
151        self.assertEqual((2, None, 'Foo', []), messages[0])
152
153    def test_extract_attribute_expr(self):
154        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
155          <input type="submit" value="${_('Save')}" />
156        </html>""")
157        translator = Translator()
158        messages = list(translator.extract(tmpl.stream))
159        self.assertEqual(1, len(messages))
160        self.assertEqual((2, '_', 'Save', []), messages[0])
161
162    def test_extract_non_included_attribute_interpolated(self):
163        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
164          <a href="#anchor_${num}">Foo</a>
165        </html>""")
166        translator = Translator()
167        messages = list(translator.extract(tmpl.stream))
168        self.assertEqual(1, len(messages))
169        self.assertEqual((2, None, 'Foo', []), messages[0])
170
171    def test_extract_text_from_sub(self):
172        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
173          <py:if test="foo">Foo</py:if>
174        </html>""")
175        translator = Translator()
176        messages = list(translator.extract(tmpl.stream))
177        self.assertEqual(1, len(messages))
178        self.assertEqual((2, None, 'Foo', []), messages[0])
179
180    def test_ignore_tag_with_fixed_xml_lang(self):
181        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
182          <p xml:lang="en">(c) 2007 Edgewall Software</p>
183        </html>""")
184        translator = Translator()
185        messages = list(translator.extract(tmpl.stream))
186        self.assertEqual(0, len(messages))
187
188    def test_extract_tag_with_variable_xml_lang(self):
189        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
190          <p xml:lang="${lang}">(c) 2007 Edgewall Software</p>
191        </html>""")
192        translator = Translator()
193        messages = list(translator.extract(tmpl.stream))
194        self.assertEqual(1, len(messages))
195        self.assertEqual((2, None, '(c) 2007 Edgewall Software', []),
196                         messages[0])
197
198    def test_ignore_attribute_with_expression(self):
199        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
200          <input type="submit" value="Reply" title="Reply to comment $num" />
201        </html>""")
202        translator = Translator()
203        messages = list(translator.extract(tmpl.stream))
204        self.assertEqual(0, len(messages))
205
206    def test_translate_with_translations_object(self):
207        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
208            xmlns:i18n="http://genshi.edgewall.org/i18n">
209          <p i18n:msg="" i18n:comment="As in foo bar">Foo</p>
210        </html>""")
211        translator = Translator(DummyTranslations({'Foo': 'Voh'}))
212        translator.setup(tmpl)
213        self.assertEqual("""<html>
214          <p>Voh</p>
215        </html>""", tmpl.generate().render())
216
217
218class MsgDirectiveTestCase(unittest.TestCase):
219
220    def test_extract_i18n_msg(self):
221        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
222            xmlns:i18n="http://genshi.edgewall.org/i18n">
223          <p i18n:msg="">
224            Please see <a href="help.html">Help</a> for details.
225          </p>
226        </html>""")
227        translator = Translator()
228        tmpl.add_directives(Translator.NAMESPACE, translator)
229        messages = list(translator.extract(tmpl.stream))
230        self.assertEqual(1, len(messages))
231        self.assertEqual('Please see [1:Help] for details.', messages[0][2])
232
233    def test_translate_i18n_msg(self):
234        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
235            xmlns:i18n="http://genshi.edgewall.org/i18n">
236          <p i18n:msg="">
237            Please see <a href="help.html">Help</a> for details.
238          </p>
239        </html>""")
240        gettext = lambda s: u"Für Details siehe bitte [1:Hilfe]."
241        translator = Translator(gettext)
242        translator.setup(tmpl)
243        self.assertEqual(u"""<html>
244          <p>Für Details siehe bitte <a href="help.html">Hilfe</a>.</p>
245        </html>""".encode('utf-8'), tmpl.generate().render(encoding='utf-8'))
246
247    def test_extract_i18n_msg_nonewline(self):
248        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
249            xmlns:i18n="http://genshi.edgewall.org/i18n">
250          <p i18n:msg="">Please see <a href="help.html">Help</a></p>
251        </html>""")
252        translator = Translator()
253        tmpl.add_directives(Translator.NAMESPACE, translator)
254        messages = list(translator.extract(tmpl.stream))
255        self.assertEqual(1, len(messages))
256        self.assertEqual('Please see [1:Help]', messages[0][2])
257
258    def test_translate_i18n_msg_nonewline(self):
259        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
260            xmlns:i18n="http://genshi.edgewall.org/i18n">
261          <p i18n:msg="">Please see <a href="help.html">Help</a></p>
262        </html>""")
263        gettext = lambda s: u"Für Details siehe bitte [1:Hilfe]"
264        translator = Translator(gettext)
265        translator.setup(tmpl)
266        self.assertEqual(u"""<html>
267          <p>Für Details siehe bitte <a href="help.html">Hilfe</a></p>
268        </html>""", tmpl.generate().render())
269
270    def test_extract_i18n_msg_elt_nonewline(self):
271        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
272            xmlns:i18n="http://genshi.edgewall.org/i18n">
273          <i18n:msg>Please see <a href="help.html">Help</a></i18n:msg>
274        </html>""")
275        translator = Translator()
276        tmpl.add_directives(Translator.NAMESPACE, translator)
277        messages = list(translator.extract(tmpl.stream))
278        self.assertEqual(1, len(messages))
279        self.assertEqual('Please see [1:Help]', messages[0][2])
280
281    def test_translate_i18n_msg_elt_nonewline(self):
282        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
283            xmlns:i18n="http://genshi.edgewall.org/i18n">
284          <i18n:msg>Please see <a href="help.html">Help</a></i18n:msg>
285        </html>""")
286        gettext = lambda s: u"Für Details siehe bitte [1:Hilfe]"
287        translator = Translator(gettext)
288        translator.setup(tmpl)
289        self.assertEqual(u"""<html>
290          Für Details siehe bitte <a href="help.html">Hilfe</a>
291        </html>""".encode('utf-8'), tmpl.generate().render(encoding='utf-8'))
292
293    def test_extract_i18n_msg_with_attributes(self):
294        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
295            xmlns:i18n="http://genshi.edgewall.org/i18n">
296          <p i18n:msg="" title="A helpful paragraph">
297            Please see <a href="help.html" title="Click for help">Help</a>
298          </p>
299        </html>""")
300        translator = Translator()
301        translator.setup(tmpl)
302        messages = list(translator.extract(tmpl.stream))
303        self.assertEqual(3, len(messages))
304        self.assertEqual('A helpful paragraph', messages[0][2])
305        self.assertEqual(3, messages[0][0])
306        self.assertEqual('Click for help', messages[1][2])
307        self.assertEqual(4, messages[1][0])
308        self.assertEqual('Please see [1:Help]', messages[2][2])
309        self.assertEqual(3, messages[2][0])
310
311    def test_translate_i18n_msg_with_attributes(self):
312        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
313            xmlns:i18n="http://genshi.edgewall.org/i18n">
314          <p i18n:msg="" title="A helpful paragraph">
315            Please see <a href="help.html" title="Click for help">Help</a>
316          </p>
317        </html>""")
318        translator = Translator(lambda msgid: {
319            'A helpful paragraph': 'Ein hilfreicher Absatz',
320            'Click for help': u'Klicken für Hilfe',
321            'Please see [1:Help]': u'Siehe bitte [1:Hilfe]'
322        }[msgid])
323        translator.setup(tmpl)
324        self.assertEqual(u"""<html>
325          <p title="Ein hilfreicher Absatz">Siehe bitte <a href="help.html" title="Klicken für Hilfe">Hilfe</a></p>
326        </html>""", tmpl.generate().render(encoding=None))
327
328    def test_extract_i18n_msg_with_dynamic_attributes(self):
329        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
330            xmlns:i18n="http://genshi.edgewall.org/i18n">
331          <p i18n:msg="" title="${_('A helpful paragraph')}">
332            Please see <a href="help.html" title="${_('Click for help')}">Help</a>
333          </p>
334        </html>""")
335        translator = Translator()
336        translator.setup(tmpl)
337        messages = list(translator.extract(tmpl.stream))
338        self.assertEqual(3, len(messages))
339        self.assertEqual('A helpful paragraph', messages[0][2])
340        self.assertEqual(3, messages[0][0])
341        self.assertEqual('Click for help', messages[1][2])
342        self.assertEqual(4, messages[1][0])
343        self.assertEqual('Please see [1:Help]', messages[2][2])
344        self.assertEqual(3, messages[2][0])
345
346    def test_translate_i18n_msg_with_dynamic_attributes(self):
347        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
348            xmlns:i18n="http://genshi.edgewall.org/i18n">
349          <p i18n:msg="" title="${_('A helpful paragraph')}">
350            Please see <a href="help.html" title="${_('Click for help')}">Help</a>
351          </p>
352        </html>""")
353        translator = Translator(lambda msgid: {
354            'A helpful paragraph': 'Ein hilfreicher Absatz',
355            'Click for help': u'Klicken für Hilfe',
356            'Please see [1:Help]': u'Siehe bitte [1:Hilfe]'
357        }[msgid])
358        translator.setup(tmpl)
359        self.assertEqual(u"""<html>
360          <p title="Ein hilfreicher Absatz">Siehe bitte <a href="help.html" title="Klicken für Hilfe">Hilfe</a></p>
361        </html>""", tmpl.generate(_=translator.translate).render(encoding=None))
362
363    def test_extract_i18n_msg_as_element_with_attributes(self):
364        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
365            xmlns:i18n="http://genshi.edgewall.org/i18n">
366          <i18n:msg params="">
367            Please see <a href="help.html" title="Click for help">Help</a>
368          </i18n:msg>
369        </html>""")
370        translator = Translator()
371        translator.setup(tmpl)
372        messages = list(translator.extract(tmpl.stream))
373        self.assertEqual(2, len(messages))
374        self.assertEqual('Click for help', messages[0][2])
375        self.assertEqual(4, messages[0][0])
376        self.assertEqual('Please see [1:Help]', messages[1][2])
377        self.assertEqual(3, messages[1][0])
378
379    def test_translate_i18n_msg_as_element_with_attributes(self):
380        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
381            xmlns:i18n="http://genshi.edgewall.org/i18n">
382          <i18n:msg params="">
383            Please see <a href="help.html" title="Click for help">Help</a>
384          </i18n:msg>
385        </html>""")
386        translator = Translator(lambda msgid: {
387            'Click for help': u'Klicken für Hilfe',
388            'Please see [1:Help]': u'Siehe bitte [1:Hilfe]'
389        }[msgid])
390        translator.setup(tmpl)
391        self.assertEqual(u"""<html>
392          Siehe bitte <a href="help.html" title="Klicken für Hilfe">Hilfe</a>
393        </html>""", tmpl.generate().render(encoding=None))
394
395    def test_extract_i18n_msg_nested(self):
396        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
397            xmlns:i18n="http://genshi.edgewall.org/i18n">
398          <p i18n:msg="">
399            Please see <a href="help.html"><em>Help</em> page</a> for details.
400          </p>
401        </html>""")
402        translator = Translator()
403        tmpl.add_directives(Translator.NAMESPACE, translator)
404        messages = list(translator.extract(tmpl.stream))
405        self.assertEqual(1, len(messages))
406        self.assertEqual('Please see [1:[2:Help] page] for details.',
407                         messages[0][2])
408
409    def test_translate_i18n_msg_nested(self):
410        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
411            xmlns:i18n="http://genshi.edgewall.org/i18n">
412          <p i18n:msg="">
413            Please see <a href="help.html"><em>Help</em> page</a> for details.
414          </p>
415        </html>""")
416        gettext = lambda s: u"Für Details siehe bitte [1:[2:Hilfeseite]]."
417        translator = Translator(gettext)
418        translator.setup(tmpl)
419        self.assertEqual(u"""<html>
420          <p>Für Details siehe bitte <a href="help.html"><em>Hilfeseite</em></a>.</p>
421        </html>""", tmpl.generate().render())
422
423    def test_extract_i18n_msg_label_with_nested_input(self):
424        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
425            xmlns:i18n="http://genshi.edgewall.org/i18n">
426          <div i18n:msg="">
427            <label><input type="text" size="3" name="daysback" value="30" /> days back</label>
428          </div>
429        </html>""")
430        translator = Translator()
431        tmpl.add_directives(Translator.NAMESPACE, translator)
432        messages = list(translator.extract(tmpl.stream))
433        self.assertEqual(1, len(messages))
434        self.assertEqual('[1:[2:] days back]',
435                         messages[0][2])
436
437    def test_translate_i18n_msg_label_with_nested_input(self):
438        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
439            xmlns:i18n="http://genshi.edgewall.org/i18n">
440          <div i18n:msg="">
441            <label><input type="text" size="3" name="daysback" value="30" /> foo bar</label>
442          </div>
443        </html>""")
444        gettext = lambda s: "[1:[2:] foo bar]"
445        translator = Translator(gettext)
446        translator.setup(tmpl)
447        self.assertEqual("""<html>
448          <div><label><input type="text" size="3" name="daysback" value="30"/> foo bar</label></div>
449        </html>""", tmpl.generate().render())
450
451    def test_extract_i18n_msg_empty(self):
452        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
453            xmlns:i18n="http://genshi.edgewall.org/i18n">
454          <p i18n:msg="">
455            Show me <input type="text" name="num" /> entries per page.
456          </p>
457        </html>""")
458        translator = Translator()
459        tmpl.add_directives(Translator.NAMESPACE, translator)
460        messages = list(translator.extract(tmpl.stream))
461        self.assertEqual(1, len(messages))
462        self.assertEqual('Show me [1:] entries per page.', messages[0][2])
463
464    def test_translate_i18n_msg_empty(self):
465        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
466            xmlns:i18n="http://genshi.edgewall.org/i18n">
467          <p i18n:msg="">
468            Show me <input type="text" name="num" /> entries per page.
469          </p>
470        </html>""")
471        gettext = lambda s: u"[1:] Einträge pro Seite anzeigen."
472        translator = Translator(gettext)
473        translator.setup(tmpl)
474        self.assertEqual(u"""<html>
475          <p><input type="text" name="num"/> Einträge pro Seite anzeigen.</p>
476        </html>""", tmpl.generate().render())
477
478    def test_extract_i18n_msg_multiple(self):
479        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
480            xmlns:i18n="http://genshi.edgewall.org/i18n">
481          <p i18n:msg="">
482            Please see <a href="help.html">Help</a> for <em>details</em>.
483          </p>
484        </html>""")
485        translator = Translator()
486        tmpl.add_directives(Translator.NAMESPACE, translator)
487        messages = list(translator.extract(tmpl.stream))
488        self.assertEqual(1, len(messages))
489        self.assertEqual('Please see [1:Help] for [2:details].', messages[0][2])
490
491    def test_translate_i18n_msg_multiple(self):
492        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
493            xmlns:i18n="http://genshi.edgewall.org/i18n">
494          <p i18n:msg="">
495            Please see <a href="help.html">Help</a> for <em>details</em>.
496          </p>
497        </html>""")
498        gettext = lambda s: u"Für [2:Details] siehe bitte [1:Hilfe]."
499        translator = Translator(gettext)
500        translator.setup(tmpl)
501        self.assertEqual(u"""<html>
502          <p>Für <em>Details</em> siehe bitte <a href="help.html">Hilfe</a>.</p>
503        </html>""", tmpl.generate().render())
504
505    def test_extract_i18n_msg_multiple_empty(self):
506        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
507            xmlns:i18n="http://genshi.edgewall.org/i18n">
508          <p i18n:msg="">
509            Show me <input type="text" name="num" /> entries per page, starting at page <input type="text" name="num" />.
510          </p>
511        </html>""")
512        translator = Translator()
513        tmpl.add_directives(Translator.NAMESPACE, translator)
514        messages = list(translator.extract(tmpl.stream))
515        self.assertEqual(1, len(messages))
516        self.assertEqual('Show me [1:] entries per page, starting at page [2:].',
517                         messages[0][2])
518
519    def test_translate_i18n_msg_multiple_empty(self):
520        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
521            xmlns:i18n="http://genshi.edgewall.org/i18n">
522          <p i18n:msg="">
523            Show me <input type="text" name="num" /> entries per page, starting at page <input type="text" name="num" />.
524          </p>
525        </html>""", encoding='utf-8')
526        gettext = lambda s: u"[1:] Einträge pro Seite, beginnend auf Seite [2:]."
527        translator = Translator(gettext)
528        translator.setup(tmpl)
529        self.assertEqual(u"""<html>
530          <p><input type="text" name="num"/> Eintr\u00E4ge pro Seite, beginnend auf Seite <input type="text" name="num"/>.</p>
531        </html>""".encode('utf-8'), tmpl.generate().render(encoding='utf-8'))
532
533    def test_extract_i18n_msg_with_param(self):
534        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
535            xmlns:i18n="http://genshi.edgewall.org/i18n">
536          <p i18n:msg="name">
537            Hello, ${user.name}!
538          </p>
539        </html>""")
540        translator = Translator()
541        tmpl.add_directives(Translator.NAMESPACE, translator)
542        messages = list(translator.extract(tmpl.stream))
543        self.assertEqual(1, len(messages))
544        self.assertEqual('Hello, %(name)s!', messages[0][2])
545
546    def test_translate_i18n_msg_with_param(self):
547        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
548            xmlns:i18n="http://genshi.edgewall.org/i18n">
549          <p i18n:msg="name">
550            Hello, ${user.name}!
551          </p>
552        </html>""")
553        gettext = lambda s: u"Hallo, %(name)s!"
554        translator = Translator(gettext)
555        translator.setup(tmpl)
556        self.assertEqual("""<html>
557          <p>Hallo, Jim!</p>
558        </html>""", tmpl.generate(user=dict(name='Jim')).render())
559
560    def test_translate_i18n_msg_with_param_reordered(self):
561        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
562            xmlns:i18n="http://genshi.edgewall.org/i18n">
563          <p i18n:msg="name">
564            Hello, ${user.name}!
565          </p>
566        </html>""")
567        gettext = lambda s: u"%(name)s, sei gegrüßt!"
568        translator = Translator(gettext)
569        translator.setup(tmpl)
570        self.assertEqual(u"""<html>
571          <p>Jim, sei gegrüßt!</p>
572        </html>""", tmpl.generate(user=dict(name='Jim')).render())
573
574    def test_translate_i18n_msg_with_attribute_param(self):
575        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
576            xmlns:i18n="http://genshi.edgewall.org/i18n">
577          <p i18n:msg="">
578            Hello, <a href="#${anchor}">dude</a>!
579          </p>
580        </html>""")
581        gettext = lambda s: u"Sei gegrüßt, [1:Alter]!"
582        translator = Translator(gettext)
583        translator.setup(tmpl)
584        self.assertEqual(u"""<html>
585          <p>Sei gegrüßt, <a href="#42">Alter</a>!</p>
586        </html>""", tmpl.generate(anchor='42').render())
587
588    def test_extract_i18n_msg_with_two_params(self):
589        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
590            xmlns:i18n="http://genshi.edgewall.org/i18n">
591          <p i18n:msg="name, time">
592            Posted by ${post.author} at ${entry.time.strftime('%H:%m')}
593          </p>
594        </html>""")
595        translator = Translator()
596        tmpl.add_directives(Translator.NAMESPACE, translator)
597        messages = list(translator.extract(tmpl.stream))
598        self.assertEqual(1, len(messages))
599        self.assertEqual('Posted by %(name)s at %(time)s', messages[0][2])
600
601    def test_translate_i18n_msg_with_two_params(self):
602        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
603            xmlns:i18n="http://genshi.edgewall.org/i18n">
604          <p i18n:msg="name, time">
605            Written by ${entry.author} at ${entry.time.strftime('%H:%M')}
606          </p>
607        </html>""")
608        gettext = lambda s: u"%(name)s schrieb dies um %(time)s"
609        translator = Translator(gettext)
610        translator.setup(tmpl)
611        entry = {
612            'author': 'Jim',
613            'time': datetime(2008, 4, 1, 14, 30)
614        }
615        self.assertEqual("""<html>
616          <p>Jim schrieb dies um 14:30</p>
617        </html>""", tmpl.generate(entry=entry).render())
618
619    def test_extract_i18n_msg_with_directive(self):
620        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
621            xmlns:i18n="http://genshi.edgewall.org/i18n">
622          <p i18n:msg="">
623            Show me <input type="text" name="num" py:attrs="{'value': x}" /> entries per page.
624          </p>
625        </html>""")
626        translator = Translator()
627        tmpl.add_directives(Translator.NAMESPACE, translator)
628        messages = list(translator.extract(tmpl.stream))
629        self.assertEqual(1, len(messages))
630        self.assertEqual('Show me [1:] entries per page.', messages[0][2])
631
632    def test_translate_i18n_msg_with_directive(self):
633        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
634            xmlns:i18n="http://genshi.edgewall.org/i18n">
635          <p i18n:msg="">
636            Show me <input type="text" name="num" py:attrs="{'value': 'x'}" /> entries per page.
637          </p>
638        </html>""")
639        gettext = lambda s: u"[1:] Einträge pro Seite anzeigen."
640        translator = Translator(gettext)
641        translator.setup(tmpl)
642        self.assertEqual(u"""<html>
643          <p><input type="text" name="num" value="x"/> Einträge pro Seite anzeigen.</p>
644        </html>""", tmpl.generate().render())
645
646    def test_extract_i18n_msg_with_comment(self):
647        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
648            xmlns:i18n="http://genshi.edgewall.org/i18n">
649          <p i18n:comment="As in foo bar" i18n:msg="">Foo</p>
650        </html>""")
651        translator = Translator()
652        tmpl.add_directives(Translator.NAMESPACE, translator)
653        messages = list(translator.extract(tmpl.stream))
654        self.assertEqual(1, len(messages))
655        self.assertEqual((3, None, 'Foo', ['As in foo bar']), messages[0])
656        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
657            xmlns:i18n="http://genshi.edgewall.org/i18n">
658          <p i18n:msg="" i18n:comment="As in foo bar">Foo</p>
659        </html>""")
660        translator = Translator()
661        tmpl.add_directives(Translator.NAMESPACE, translator)
662        messages = list(translator.extract(tmpl.stream))
663        self.assertEqual(1, len(messages))
664        self.assertEqual((3, None, 'Foo', ['As in foo bar']), messages[0])
665
666    def test_translate_i18n_msg_with_comment(self):
667        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
668            xmlns:i18n="http://genshi.edgewall.org/i18n">
669          <p i18n:msg="" i18n:comment="As in foo bar">Foo</p>
670        </html>""")
671        gettext = lambda s: u"Voh"
672        translator = Translator(gettext)
673        translator.setup(tmpl)
674        self.assertEqual("""<html>
675          <p>Voh</p>
676        </html>""", tmpl.generate().render())
677
678    def test_extract_i18n_msg_with_attr(self):
679        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
680            xmlns:i18n="http://genshi.edgewall.org/i18n">
681          <p i18n:msg="" title="Foo bar">Foo</p>
682        </html>""")
683        translator = Translator()
684        messages = list(translator.extract(tmpl.stream))
685        self.assertEqual(2, len(messages))
686        self.assertEqual((3, None, 'Foo bar', []), messages[0])
687        self.assertEqual((3, None, 'Foo', []), messages[1])
688
689    def test_translate_i18n_msg_with_attr(self):
690        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
691            xmlns:i18n="http://genshi.edgewall.org/i18n">
692          <p i18n:msg="" title="Foo bar">Foo</p>
693        </html>""")
694        gettext = lambda s: u"Voh"
695        translator = Translator(DummyTranslations({
696            'Foo': 'Voh',
697            'Foo bar': u'Voh bär'
698        }))
699        tmpl.filters.insert(0, translator)
700        tmpl.add_directives(Translator.NAMESPACE, translator)
701        self.assertEqual(u"""<html>
702          <p title="Voh bär">Voh</p>
703        </html>""", tmpl.generate().render())
704
705    def test_translate_i18n_msg_and_py_strip_directives(self):
706        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
707            xmlns:i18n="http://genshi.edgewall.org/i18n">
708          <p i18n:msg="" py:strip="">Foo</p>
709          <p py:strip="" i18n:msg="">Foo</p>
710        </html>""")
711        translator = Translator(DummyTranslations({'Foo': 'Voh'}))
712        translator.setup(tmpl)
713        self.assertEqual("""<html>
714          Voh
715          Voh
716        </html>""", tmpl.generate().render())
717
718    def test_i18n_msg_ticket_300_extract(self):
719        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
720            xmlns:i18n="http://genshi.edgewall.org/i18n">
721          <i18n:msg params="date, author">
722            Changed ${ '10/12/2008' } ago by ${ 'me, the author' }
723          </i18n:msg>
724        </html>""")
725        translator = Translator()
726        tmpl.add_directives(Translator.NAMESPACE, translator)
727        messages = list(translator.extract(tmpl.stream))
728        self.assertEqual(1, len(messages))
729        self.assertEqual(
730            (3, None, 'Changed %(date)s ago by %(author)s', []), messages[0]
731        )
732
733    def test_i18n_msg_ticket_300_translate(self):
734        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
735            xmlns:i18n="http://genshi.edgewall.org/i18n">
736          <i18n:msg params="date, author">
737            Changed ${ date } ago by ${ author }
738          </i18n:msg>
739        </html>""")
740        translations = DummyTranslations({
741            'Changed %(date)s ago by %(author)s': u'Modificado à %(date)s por %(author)s'
742        })
743        translator = Translator(translations)
744        translator.setup(tmpl)
745        self.assertEqual(u"""<html>
746          Modificado à um dia por Pedro
747        </html>""".encode('utf-8'), tmpl.generate(date='um dia', author="Pedro").render(encoding='utf-8'))
748
749
750    def test_i18n_msg_ticket_251_extract(self):
751        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
752            xmlns:i18n="http://genshi.edgewall.org/i18n">
753          <p i18n:msg=""><tt><b>Translation[&nbsp;0&nbsp;]</b>: <em>One coin</em></tt></p>
754        </html>""")
755        translator = Translator()
756        tmpl.add_directives(Translator.NAMESPACE, translator)
757        messages = list(translator.extract(tmpl.stream))
758        self.assertEqual(1, len(messages))
759        self.assertEqual(
760            (3, None, u'[1:[2:Translation\\[\xa00\xa0\\]]: [3:One coin]]', []), messages[0]
761        )
762
763    def test_i18n_msg_ticket_251_translate(self):
764        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
765            xmlns:i18n="http://genshi.edgewall.org/i18n">
766          <p i18n:msg=""><tt><b>Translation[&nbsp;0&nbsp;]</b>: <em>One coin</em></tt></p>
767        </html>""")
768        translations = DummyTranslations({
769            u'[1:[2:Translation\\[\xa00\xa0\\]]: [3:One coin]]':
770                u'[1:[2:Trandução\\[\xa00\xa0\\]]: [3:Uma moeda]]'
771        })
772        translator = Translator(translations)
773        translator.setup(tmpl)
774        self.assertEqual(u"""<html>
775          <p><tt><b>Trandução[ 0 ]</b>: <em>Uma moeda</em></tt></p>
776        </html>""".encode('utf-8'), tmpl.generate().render(encoding='utf-8'))
777
778    def test_extract_i18n_msg_with_other_directives_nested(self):
779        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
780            xmlns:i18n="http://genshi.edgewall.org/i18n">
781          <p i18n:msg="" py:with="q = quote_plus(message[:80])">Before you do that, though, please first try
782            <strong><a href="${trac.homepage}search?ticket=yes&amp;noquickjump=1&amp;q=$q">searching</a>
783            for similar issues</strong>, as it is quite likely that this problem
784            has been reported before. For questions about installation
785            and configuration of Trac, please try the
786            <a href="${trac.homepage}wiki/MailingList">mailing list</a>
787            instead of filing a ticket.
788          </p>
789        </html>""")
790        translator = Translator()
791        translator.setup(tmpl)
792        messages = list(translator.extract(tmpl.stream))
793        self.assertEqual(1, len(messages))
794        self.assertEqual(
795            'Before you do that, though, please first try\n            '
796            '[1:[2:searching]\n            for similar issues], as it is '
797            'quite likely that this problem\n            has been reported '
798            'before. For questions about installation\n            and '
799            'configuration of Trac, please try the\n            '
800            '[3:mailing list]\n            instead of filing a ticket.',
801            messages[0][2]
802        )
803
804    def test_translate_i18n_msg_with_other_directives_nested(self):
805        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
806            xmlns:i18n="http://genshi.edgewall.org/i18n">
807          <p i18n:msg="">Before you do that, though, please first try
808            <strong><a href="${trac.homepage}search?ticket=yes&amp;noquickjump=1&amp;q=q">searching</a>
809            for similar issues</strong>, as it is quite likely that this problem
810            has been reported before. For questions about installation
811            and configuration of Trac, please try the
812            <a href="${trac.homepage}wiki/MailingList">mailing list</a>
813            instead of filing a ticket.
814          </p>
815        </html>""")
816        translations = DummyTranslations({
817            'Before you do that, though, please first try\n            '
818            '[1:[2:searching]\n            for similar issues], as it is '
819            'quite likely that this problem\n            has been reported '
820            'before. For questions about installation\n            and '
821            'configuration of Trac, please try the\n            '
822            '[3:mailing list]\n            instead of filing a ticket.':
823                u'Antes de o fazer, porém,\n            '
824                u'[1:por favor tente [2:procurar]\n            por problemas semelhantes], uma vez que '
825                u'é muito provável que este problema\n            já tenha sido reportado '
826                u'anteriormente. Para questões relativas à instalação\n            e '
827                u'configuração do Trac, por favor tente a\n            '
828                u'[3:mailing list]\n            em vez de criar um assunto.'
829        })
830        translator = Translator(translations)
831        translator.setup(tmpl)
832        messages = list(translator.extract(tmpl.stream))
833        self.assertEqual(1, len(messages))
834        ctx = Context()
835        ctx.push({'trac': {'homepage': 'http://trac.edgewall.org/'}})
836        self.assertEqual(u"""<html>
837          <p>Antes de o fazer, porém,
838            <strong>por favor tente <a href="http://trac.edgewall.org/search?ticket=yes&amp;noquickjump=1&amp;q=q">procurar</a>
839            por problemas semelhantes</strong>, uma vez que é muito provável que este problema
840            já tenha sido reportado anteriormente. Para questões relativas à instalação
841            e configuração do Trac, por favor tente a
842            <a href="http://trac.edgewall.org/wiki/MailingList">mailing list</a>
843            em vez de criar um assunto.</p>
844        </html>""", tmpl.generate(ctx).render())
845
846    def test_i18n_msg_with_other_nested_directives_with_reordered_content(self):
847        # See: http://genshi.edgewall.org/ticket/300#comment:10
848        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
849            xmlns:i18n="http://genshi.edgewall.org/i18n">
850          <p py:if="not editable" class="hint" i18n:msg="">
851            <strong>Note:</strong> This repository is defined in
852            <code><a href="${ 'href.wiki(TracIni)' }">trac.ini</a></code>
853            and cannot be edited on this page.
854          </p>
855        </html>""")
856        translations = DummyTranslations({
857            '[1:Note:] This repository is defined in\n            '
858            '[2:[3:trac.ini]]\n            and cannot be edited on this page.':
859                u'[1:Nota:] Este repositório está definido em \n           '
860                u'[2:[3:trac.ini]]\n            e não pode ser editado nesta página.',
861        })
862        translator = Translator(translations)
863        translator.setup(tmpl)
864        messages = list(translator.extract(tmpl.stream))
865        self.assertEqual(1, len(messages))
866        self.assertEqual(
867            '[1:Note:] This repository is defined in\n            '
868            '[2:[3:trac.ini]]\n            and cannot be edited on this page.',
869            messages[0][2]
870        )
871        self.assertEqual(u"""<html>
872          <p class="hint"><strong>Nota:</strong> Este repositório está definido em
873           <code><a href="href.wiki(TracIni)">trac.ini</a></code>
874            e não pode ser editado nesta página.</p>
875        </html>""".encode('utf-8'), tmpl.generate(editable=False).render(encoding='utf-8'))
876
877    def test_extract_i18n_msg_with_py_strip(self):
878        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
879            xmlns:i18n="http://genshi.edgewall.org/i18n">
880          <p i18n:msg="" py:strip="">
881            Please see <a href="help.html">Help</a> for details.
882          </p>
883        </html>""")
884        translator = Translator()
885        tmpl.add_directives(Translator.NAMESPACE, translator)
886        messages = list(translator.extract(tmpl.stream))
887        self.assertEqual(1, len(messages))
888        self.assertEqual((3, None, 'Please see [1:Help] for details.', []),
889                         messages[0])
890
891    def test_extract_i18n_msg_with_py_strip_and_comment(self):
892        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
893            xmlns:i18n="http://genshi.edgewall.org/i18n">
894          <p i18n:msg="" py:strip="" i18n:comment="Foo">
895            Please see <a href="help.html">Help</a> for details.
896          </p>
897        </html>""")
898        translator = Translator()
899        tmpl.add_directives(Translator.NAMESPACE, translator)
900        messages = list(translator.extract(tmpl.stream))
901        self.assertEqual(1, len(messages))
902        self.assertEqual((3, None, 'Please see [1:Help] for details.',
903                          ['Foo']), messages[0])
904
905    def test_translate_i18n_msg_and_comment_with_py_strip_directives(self):
906        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
907            xmlns:i18n="http://genshi.edgewall.org/i18n">
908          <p i18n:msg="" i18n:comment="As in foo bar" py:strip="">Foo</p>
909          <p py:strip="" i18n:msg="" i18n:comment="As in foo bar">Foo</p>
910        </html>""")
911        translator = Translator(DummyTranslations({'Foo': 'Voh'}))
912        translator.setup(tmpl)
913        self.assertEqual("""<html>
914          Voh
915          Voh
916        </html>""", tmpl.generate().render())
917
918    def test_translate_i18n_msg_ticket_404(self):
919        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
920            xmlns:i18n="http://genshi.edgewall.org/i18n">
921          <p i18n:msg="first,second">
922            $first <span>$second</span> KEPT <span>Inside a tag</span> tail
923          </p></html>""")
924        translator = Translator(DummyTranslations())
925        translator.setup(tmpl)
926        self.assertEqual("""<html>
927          <p>FIRST <span>SECOND</span> KEPT <span>Inside a tag</span> tail"""
928          """</p></html>""",
929          tmpl.generate(first="FIRST", second="SECOND").render())
930
931    def test_translate_i18n_msg_ticket_404_regression(self):
932        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
933            xmlns:i18n="http://genshi.edgewall.org/i18n">
934          <h1 i18n:msg="name">text <a>$name</a></h1>
935        </html>""")
936        gettext = lambda s: u'head [1:%(name)s] tail'
937        translator = Translator(gettext)
938        translator.setup(tmpl)
939        self.assertEqual("""<html>
940          <h1>head <a>NAME</a> tail</h1>
941        </html>""", tmpl.generate(name='NAME').render())
942
943
944class ChooseDirectiveTestCase(unittest.TestCase):
945
946    def test_translate_i18n_choose_as_attribute(self):
947        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
948            xmlns:i18n="http://genshi.edgewall.org/i18n">
949          <div i18n:choose="one">
950            <p i18n:singular="">FooBar</p>
951            <p i18n:plural="">FooBars</p>
952          </div>
953          <div i18n:choose="two">
954            <p i18n:singular="">FooBar</p>
955            <p i18n:plural="">FooBars</p>
956          </div>
957        </html>""")
958        translations = DummyTranslations()
959        translator = Translator(translations)
960        translator.setup(tmpl)
961        self.assertEqual("""<html>
962          <div>
963            <p>FooBar</p>
964          </div>
965          <div>
966            <p>FooBars</p>
967          </div>
968        </html>""", tmpl.generate(one=1, two=2).render())
969
970    def test_translate_i18n_choose_as_directive(self):
971        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
972            xmlns:i18n="http://genshi.edgewall.org/i18n">
973        <i18n:choose numeral="two">
974          <p i18n:singular="">FooBar</p>
975          <p i18n:plural="">FooBars</p>
976        </i18n:choose>
977        <i18n:choose numeral="one">
978          <p i18n:singular="">FooBar</p>
979          <p i18n:plural="">FooBars</p>
980        </i18n:choose>
981        </html>""")
982        translations = DummyTranslations()
983        translator = Translator(translations)
984        translator.setup(tmpl)
985        self.assertEqual("""<html>
986          <p>FooBars</p>
987          <p>FooBar</p>
988        </html>""", tmpl.generate(one=1, two=2).render())
989
990    def test_translate_i18n_choose_as_directive_singular_and_plural_with_strip(self):
991        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
992            xmlns:i18n="http://genshi.edgewall.org/i18n">
993        <i18n:choose numeral="two">
994          <p i18n:singular="" py:strip="">FooBar Singular with Strip</p>
995          <p i18n:plural="">FooBars Plural without Strip</p>
996        </i18n:choose>
997        <i18n:choose numeral="two">
998          <p i18n:singular="">FooBar singular without strip</p>
999          <p i18n:plural="" py:strip="">FooBars plural with strip</p>
1000        </i18n:choose>
1001        <i18n:choose numeral="one">
1002          <p i18n:singular="">FooBar singular without strip</p>
1003          <p i18n:plural="" py:strip="">FooBars plural with strip</p>
1004        </i18n:choose>
1005        <i18n:choose numeral="one">
1006          <p i18n:singular="" py:strip="">FooBar singular with strip</p>
1007          <p i18n:plural="">FooBars plural without strip</p>
1008        </i18n:choose>
1009        </html>""")
1010        translations = DummyTranslations()
1011        translator = Translator(translations)
1012        translator.setup(tmpl)
1013        self.assertEqual("""<html>
1014          <p>FooBars Plural without Strip</p>
1015          FooBars plural with strip
1016          <p>FooBar singular without strip</p>
1017          FooBar singular with strip
1018        </html>""", tmpl.generate(one=1, two=2).render())
1019
1020    def test_translate_i18n_choose_plural_singular_as_directive(self):
1021        # Ticket 371
1022        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1023            xmlns:i18n="http://genshi.edgewall.org/i18n">
1024        <i18n:choose numeral="two">
1025          <i18n:singular>FooBar</i18n:singular>
1026          <i18n:plural>FooBars</i18n:plural>
1027        </i18n:choose>
1028        <i18n:choose numeral="one">
1029          <i18n:singular>FooBar</i18n:singular>
1030          <i18n:plural>FooBars</i18n:plural>
1031        </i18n:choose>
1032        </html>""")
1033        translations = DummyTranslations({
1034            ('FooBar', 0): 'FuBar',
1035            ('FooBars', 1): 'FuBars',
1036            'FooBar': 'FuBar',
1037            'FooBars': 'FuBars',
1038        })
1039        translator = Translator(translations)
1040        translator.setup(tmpl)
1041        self.assertEqual("""<html>
1042          FuBars
1043          FuBar
1044        </html>""", tmpl.generate(one=1, two=2).render())
1045
1046    def test_translate_i18n_choose_as_attribute_with_params(self):
1047        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1048            xmlns:i18n="http://genshi.edgewall.org/i18n">
1049          <div i18n:choose="two; fname, lname">
1050            <p i18n:singular="">Foo $fname $lname</p>
1051            <p i18n:plural="">Foos $fname $lname</p>
1052          </div>
1053        </html>""")
1054        translations = DummyTranslations({
1055            ('Foo %(fname)s %(lname)s', 0): 'Voh %(fname)s %(lname)s',
1056            ('Foo %(fname)s %(lname)s', 1): 'Vohs %(fname)s %(lname)s',
1057                 'Foo %(fname)s %(lname)s': 'Voh %(fname)s %(lname)s',
1058                'Foos %(fname)s %(lname)s': 'Vohs %(fname)s %(lname)s',
1059        })
1060        translator = Translator(translations)
1061        translator.setup(tmpl)
1062        self.assertEqual("""<html>
1063          <div>
1064            <p>Vohs John Doe</p>
1065          </div>
1066        </html>""", tmpl.generate(two=2, fname='John', lname='Doe').render())
1067
1068    def test_translate_i18n_choose_as_attribute_with_params_and_domain_as_param(self):
1069        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1070            xmlns:i18n="http://genshi.edgewall.org/i18n"
1071            i18n:domain="foo">
1072          <div i18n:choose="two; fname, lname">
1073            <p i18n:singular="">Foo $fname $lname</p>
1074            <p i18n:plural="">Foos $fname $lname</p>
1075          </div>
1076        </html>""")
1077        translations = DummyTranslations()
1078        translations.add_domain('foo', {
1079            ('Foo %(fname)s %(lname)s', 0): 'Voh %(fname)s %(lname)s',
1080            ('Foo %(fname)s %(lname)s', 1): 'Vohs %(fname)s %(lname)s',
1081                 'Foo %(fname)s %(lname)s': 'Voh %(fname)s %(lname)s',
1082                'Foos %(fname)s %(lname)s': 'Vohs %(fname)s %(lname)s',
1083        })
1084        translator = Translator(translations)
1085        translator.setup(tmpl)
1086        self.assertEqual("""<html>
1087          <div>
1088            <p>Vohs John Doe</p>
1089          </div>
1090        </html>""", tmpl.generate(two=2, fname='John', lname='Doe').render())
1091
1092    def test_translate_i18n_choose_as_directive_with_params(self):
1093        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1094            xmlns:i18n="http://genshi.edgewall.org/i18n">
1095        <i18n:choose numeral="two" params="fname, lname">
1096          <p i18n:singular="">Foo ${fname} ${lname}</p>
1097          <p i18n:plural="">Foos ${fname} ${lname}</p>
1098        </i18n:choose>
1099        <i18n:choose numeral="one" params="fname, lname">
1100          <p i18n:singular="">Foo ${fname} ${lname}</p>
1101          <p i18n:plural="">Foos ${fname} ${lname}</p>
1102        </i18n:choose>
1103        </html>""")
1104        translations = DummyTranslations({
1105            ('Foo %(fname)s %(lname)s', 0): 'Voh %(fname)s %(lname)s',
1106            ('Foo %(fname)s %(lname)s', 1): 'Vohs %(fname)s %(lname)s',
1107                 'Foo %(fname)s %(lname)s': 'Voh %(fname)s %(lname)s',
1108                'Foos %(fname)s %(lname)s': 'Vohs %(fname)s %(lname)s',
1109        })
1110        translator = Translator(translations)
1111        translator.setup(tmpl)
1112        self.assertEqual("""<html>
1113          <p>Vohs John Doe</p>
1114          <p>Voh John Doe</p>
1115        </html>""", tmpl.generate(one=1, two=2,
1116                                  fname='John', lname='Doe').render())
1117
1118    def test_translate_i18n_choose_as_directive_with_params_and_domain_as_directive(self):
1119        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1120            xmlns:i18n="http://genshi.edgewall.org/i18n">
1121        <i18n:domain name="foo">
1122        <i18n:choose numeral="two" params="fname, lname">
1123          <p i18n:singular="">Foo ${fname} ${lname}</p>
1124          <p i18n:plural="">Foos ${fname} ${lname}</p>
1125        </i18n:choose>
1126        </i18n:domain>
1127        <i18n:choose numeral="one" params="fname, lname">
1128          <p i18n:singular="">Foo ${fname} ${lname}</p>
1129          <p i18n:plural="">Foos ${fname} ${lname}</p>
1130        </i18n:choose>
1131        </html>""")
1132        translations = DummyTranslations()
1133        translations.add_domain('foo', {
1134            ('Foo %(fname)s %(lname)s', 0): 'Voh %(fname)s %(lname)s',
1135            ('Foo %(fname)s %(lname)s', 1): 'Vohs %(fname)s %(lname)s',
1136                 'Foo %(fname)s %(lname)s': 'Voh %(fname)s %(lname)s',
1137                'Foos %(fname)s %(lname)s': 'Vohs %(fname)s %(lname)s',
1138        })
1139        translator = Translator(translations)
1140        translator.setup(tmpl)
1141        self.assertEqual("""<html>
1142          <p>Vohs John Doe</p>
1143          <p>Foo John Doe</p>
1144        </html>""", tmpl.generate(one=1, two=2,
1145                                  fname='John', lname='Doe').render())
1146
1147    def test_extract_i18n_choose_as_attribute(self):
1148        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1149            xmlns:i18n="http://genshi.edgewall.org/i18n">
1150          <div i18n:choose="one">
1151            <p i18n:singular="">FooBar</p>
1152            <p i18n:plural="">FooBars</p>
1153          </div>
1154          <div i18n:choose="two">
1155            <p i18n:singular="">FooBar</p>
1156            <p i18n:plural="">FooBars</p>
1157          </div>
1158        </html>""")
1159        translator = Translator()
1160        tmpl.add_directives(Translator.NAMESPACE, translator)
1161        messages = list(translator.extract(tmpl.stream))
1162        self.assertEqual(2, len(messages))
1163        self.assertEqual((3, 'ngettext', ('FooBar', 'FooBars'), []), messages[0])
1164        self.assertEqual((7, 'ngettext', ('FooBar', 'FooBars'), []), messages[1])
1165
1166    def test_extract_i18n_choose_as_directive(self):
1167        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1168            xmlns:i18n="http://genshi.edgewall.org/i18n">
1169        <i18n:choose numeral="two">
1170          <p i18n:singular="">FooBar</p>
1171          <p i18n:plural="">FooBars</p>
1172        </i18n:choose>
1173        <i18n:choose numeral="one">
1174          <p i18n:singular="">FooBar</p>
1175          <p i18n:plural="">FooBars</p>
1176        </i18n:choose>
1177        </html>""")
1178        translator = Translator()
1179        tmpl.add_directives(Translator.NAMESPACE, translator)
1180        messages = list(translator.extract(tmpl.stream))
1181        self.assertEqual(2, len(messages))
1182        self.assertEqual((3, 'ngettext', ('FooBar', 'FooBars'), []), messages[0])
1183        self.assertEqual((7, 'ngettext', ('FooBar', 'FooBars'), []), messages[1])
1184
1185    def test_extract_i18n_choose_as_attribute_with_params(self):
1186        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1187            xmlns:i18n="http://genshi.edgewall.org/i18n">
1188          <div i18n:choose="two; fname, lname">
1189            <p i18n:singular="">Foo $fname $lname</p>
1190            <p i18n:plural="">Foos $fname $lname</p>
1191          </div>
1192        </html>""")
1193        translator = Translator()
1194        tmpl.add_directives(Translator.NAMESPACE, translator)
1195        messages = list(translator.extract(tmpl.stream))
1196        self.assertEqual(1, len(messages))
1197        self.assertEqual((3, 'ngettext', ('Foo %(fname)s %(lname)s',
1198                                          'Foos %(fname)s %(lname)s'), []),
1199                         messages[0])
1200
1201    def test_extract_i18n_choose_as_attribute_with_params_and_domain_as_param(self):
1202        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1203            xmlns:i18n="http://genshi.edgewall.org/i18n"
1204            i18n:domain="foo">
1205          <div i18n:choose="two; fname, lname">
1206            <p i18n:singular="">Foo $fname $lname</p>
1207            <p i18n:plural="">Foos $fname $lname</p>
1208          </div>
1209        </html>""")
1210        translator = Translator()
1211        tmpl.add_directives(Translator.NAMESPACE, translator)
1212        messages = list(translator.extract(tmpl.stream))
1213        self.assertEqual(1, len(messages))
1214        self.assertEqual((4, 'ngettext', ('Foo %(fname)s %(lname)s',
1215                                          'Foos %(fname)s %(lname)s'), []),
1216                         messages[0])
1217
1218    def test_extract_i18n_choose_as_directive_with_params(self):
1219        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1220            xmlns:i18n="http://genshi.edgewall.org/i18n">
1221        <i18n:choose numeral="two" params="fname, lname">
1222          <p i18n:singular="">Foo ${fname} ${lname}</p>
1223          <p i18n:plural="">Foos ${fname} ${lname}</p>
1224        </i18n:choose>
1225        <i18n:choose numeral="one" params="fname, lname">
1226          <p i18n:singular="">Foo ${fname} ${lname}</p>
1227          <p i18n:plural="">Foos ${fname} ${lname}</p>
1228        </i18n:choose>
1229        </html>""")
1230        translator = Translator()
1231        tmpl.add_directives(Translator.NAMESPACE, translator)
1232        messages = list(translator.extract(tmpl.stream))
1233        self.assertEqual(2, len(messages))
1234        self.assertEqual((3, 'ngettext', ('Foo %(fname)s %(lname)s',
1235                                          'Foos %(fname)s %(lname)s'), []),
1236                         messages[0])
1237        self.assertEqual((7, 'ngettext', ('Foo %(fname)s %(lname)s',
1238                                          'Foos %(fname)s %(lname)s'), []),
1239                         messages[1])
1240
1241    def test_extract_i18n_choose_as_directive_with_params_and_domain_as_directive(self):
1242        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1243            xmlns:i18n="http://genshi.edgewall.org/i18n">
1244        <i18n:domain name="foo">
1245        <i18n:choose numeral="two" params="fname, lname">
1246          <p i18n:singular="">Foo ${fname} ${lname}</p>
1247          <p i18n:plural="">Foos ${fname} ${lname}</p>
1248        </i18n:choose>
1249        </i18n:domain>
1250        <i18n:choose numeral="one" params="fname, lname">
1251          <p i18n:singular="">Foo ${fname} ${lname}</p>
1252          <p i18n:plural="">Foos ${fname} ${lname}</p>
1253        </i18n:choose>
1254        </html>""")
1255        translator = Translator()
1256        tmpl.add_directives(Translator.NAMESPACE, translator)
1257        messages = list(translator.extract(tmpl.stream))
1258        self.assertEqual(2, len(messages))
1259        self.assertEqual((4, 'ngettext', ('Foo %(fname)s %(lname)s',
1260                                          'Foos %(fname)s %(lname)s'), []),
1261                         messages[0])
1262        self.assertEqual((9, 'ngettext', ('Foo %(fname)s %(lname)s',
1263                                          'Foos %(fname)s %(lname)s'), []),
1264                         messages[1])
1265
1266    def test_extract_i18n_choose_as_attribute_with_params_and_comment(self):
1267        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1268            xmlns:i18n="http://genshi.edgewall.org/i18n">
1269          <div i18n:choose="two; fname, lname" i18n:comment="As in Foo Bar">
1270            <p i18n:singular="">Foo $fname $lname</p>
1271            <p i18n:plural="">Foos $fname $lname</p>
1272          </div>
1273        </html>""")
1274        translator = Translator()
1275        tmpl.add_directives(Translator.NAMESPACE, translator)
1276        messages = list(translator.extract(tmpl.stream))
1277        self.assertEqual(1, len(messages))
1278        self.assertEqual((3, 'ngettext', ('Foo %(fname)s %(lname)s',
1279                                          'Foos %(fname)s %(lname)s'),
1280                          ['As in Foo Bar']),
1281                         messages[0])
1282
1283    def test_extract_i18n_choose_as_directive_with_params_and_comment(self):
1284        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1285            xmlns:i18n="http://genshi.edgewall.org/i18n">
1286        <i18n:choose numeral="two" params="fname, lname" i18n:comment="As in Foo Bar">
1287          <p i18n:singular="">Foo ${fname} ${lname}</p>
1288          <p i18n:plural="">Foos ${fname} ${lname}</p>
1289        </i18n:choose>
1290        </html>""")
1291        translator = Translator()
1292        tmpl.add_directives(Translator.NAMESPACE, translator)
1293        messages = list(translator.extract(tmpl.stream))
1294        self.assertEqual(1, len(messages))
1295        self.assertEqual((3, 'ngettext', ('Foo %(fname)s %(lname)s',
1296                                          'Foos %(fname)s %(lname)s'),
1297                          ['As in Foo Bar']),
1298                         messages[0])
1299
1300    def test_extract_i18n_choose_with_attributes(self):
1301        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1302            xmlns:i18n="http://genshi.edgewall.org/i18n">
1303          <p i18n:choose="num; num" title="Things">
1304            <i18n:singular>
1305              There is <a href="$link" title="View thing">${num} thing</a>.
1306            </i18n:singular>
1307            <i18n:plural>
1308              There are <a href="$link" title="View things">${num} things</a>.
1309            </i18n:plural>
1310          </p>
1311        </html>""")
1312        translator = Translator()
1313        translator.setup(tmpl)
1314        messages = list(translator.extract(tmpl.stream))
1315        self.assertEqual(4, len(messages))
1316        self.assertEqual((3, None, 'Things', []), messages[0])
1317        self.assertEqual((5, None, 'View thing', []), messages[1])
1318        self.assertEqual((8, None, 'View things', []), messages[2])
1319        self.assertEqual(
1320            (3, 'ngettext', ('There is [1:%(num)s thing].',
1321                             'There are [1:%(num)s things].'), []),
1322            messages[3])
1323
1324    def test_translate_i18n_choose_with_attributes(self):
1325        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1326            xmlns:i18n="http://genshi.edgewall.org/i18n">
1327          <p i18n:choose="num; num" title="Things">
1328            <i18n:singular>
1329              There is <a href="$link" title="View thing">${num} thing</a>.
1330            </i18n:singular>
1331            <i18n:plural>
1332              There are <a href="$link" title="View things">${num} things</a>.
1333            </i18n:plural>
1334          </p>
1335        </html>""")
1336        translations = DummyTranslations({
1337            'Things': 'Sachen',
1338            'View thing': 'Sache betrachten',
1339            'View things': 'Sachen betrachten',
1340            ('There is [1:%(num)s thing].', 0): 'Da ist [1:%(num)s Sache].',
1341            ('There is [1:%(num)s thing].', 1): 'Da sind [1:%(num)s Sachen].'
1342        })
1343        translator = Translator(translations)
1344        translator.setup(tmpl)
1345        self.assertEqual(u"""<html>
1346          <p title="Sachen">
1347            Da ist <a href="/things" title="Sache betrachten">1 Sache</a>.
1348          </p>
1349        </html>""", tmpl.generate(link="/things", num=1).render(encoding=None))
1350        self.assertEqual(u"""<html>
1351          <p title="Sachen">
1352            Da sind <a href="/things" title="Sachen betrachten">3 Sachen</a>.
1353          </p>
1354        </html>""", tmpl.generate(link="/things", num=3).render(encoding=None))
1355
1356    def test_extract_i18n_choose_as_element_with_attributes(self):
1357        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1358            xmlns:i18n="http://genshi.edgewall.org/i18n">
1359          <i18n:choose numeral="num" params="num">
1360            <p i18n:singular="" title="Things">
1361              There is <a href="$link" title="View thing">${num} thing</a>.
1362            </p>
1363            <p i18n:plural="" title="Things">
1364              There are <a href="$link" title="View things">${num} things</a>.
1365            </p>
1366          </i18n:choose>
1367        </html>""")
1368        translator = Translator()
1369        translator.setup(tmpl)
1370        messages = list(translator.extract(tmpl.stream))
1371        self.assertEqual(5, len(messages))
1372        self.assertEqual((4, None, 'Things', []), messages[0])
1373        self.assertEqual((5, None, 'View thing', []), messages[1])
1374        self.assertEqual((7, None, 'Things', []), messages[2])
1375        self.assertEqual((8, None, 'View things', []), messages[3])
1376        self.assertEqual(
1377            (3, 'ngettext', ('There is [1:%(num)s thing].',
1378                             'There are [1:%(num)s things].'), []),
1379            messages[4])
1380
1381    def test_translate_i18n_choose_as_element_with_attributes(self):
1382        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1383            xmlns:i18n="http://genshi.edgewall.org/i18n">
1384          <i18n:choose numeral="num" params="num">
1385            <p i18n:singular="" title="Things">
1386              There is <a href="$link" title="View thing">${num} thing</a>.
1387            </p>
1388            <p i18n:plural="" title="Things">
1389              There are <a href="$link" title="View things">${num} things</a>.
1390            </p>
1391          </i18n:choose>
1392        </html>""")
1393        translations = DummyTranslations({
1394            'Things': 'Sachen',
1395            'View thing': 'Sache betrachten',
1396            'View things': 'Sachen betrachten',
1397            ('There is [1:%(num)s thing].', 0): 'Da ist [1:%(num)s Sache].',
1398            ('There is [1:%(num)s thing].', 1): 'Da sind [1:%(num)s Sachen].'
1399        })
1400        translator = Translator(translations)
1401        translator.setup(tmpl)
1402        self.assertEqual(u"""<html>
1403            <p title="Sachen">Da ist <a href="/things" title="Sache betrachten">1 Sache</a>.</p>
1404        </html>""", tmpl.generate(link="/things", num=1).render(encoding=None))
1405        self.assertEqual(u"""<html>
1406            <p title="Sachen">Da sind <a href="/things" title="Sachen betrachten">3 Sachen</a>.</p>
1407        </html>""", tmpl.generate(link="/things", num=3).render(encoding=None))
1408
1409    def test_translate_i18n_choose_and_py_strip(self):
1410        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1411            xmlns:i18n="http://genshi.edgewall.org/i18n">
1412          <div i18n:choose="two; fname, lname">
1413            <p i18n:singular="">Foo $fname $lname</p>
1414            <p i18n:plural="">Foos $fname $lname</p>
1415          </div>
1416        </html>""")
1417        translations = DummyTranslations({
1418            ('Foo %(fname)s %(lname)s', 0): 'Voh %(fname)s %(lname)s',
1419            ('Foo %(fname)s %(lname)s', 1): 'Vohs %(fname)s %(lname)s',
1420                 'Foo %(fname)s %(lname)s': 'Voh %(fname)s %(lname)s',
1421                'Foos %(fname)s %(lname)s': 'Vohs %(fname)s %(lname)s',
1422        })
1423        translator = Translator(translations)
1424        translator.setup(tmpl)
1425        self.assertEqual("""<html>
1426          <div>
1427            <p>Vohs John Doe</p>
1428          </div>
1429        </html>""", tmpl.generate(two=2, fname='John', lname='Doe').render())
1430
1431    def test_translate_i18n_choose_and_domain_and_py_strip(self):
1432        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1433            xmlns:i18n="http://genshi.edgewall.org/i18n"
1434            i18n:domain="foo">
1435          <div i18n:choose="two; fname, lname">
1436            <p i18n:singular="">Foo $fname $lname</p>
1437            <p i18n:plural="">Foos $fname $lname</p>
1438          </div>
1439        </html>""")
1440        translations = DummyTranslations()
1441        translations.add_domain('foo', {
1442            ('Foo %(fname)s %(lname)s', 0): 'Voh %(fname)s %(lname)s',
1443            ('Foo %(fname)s %(lname)s', 1): 'Vohs %(fname)s %(lname)s',
1444                 'Foo %(fname)s %(lname)s': 'Voh %(fname)s %(lname)s',
1445                'Foos %(fname)s %(lname)s': 'Vohs %(fname)s %(lname)s',
1446        })
1447        translator = Translator(translations)
1448        translator.setup(tmpl)
1449        self.assertEqual("""<html>
1450          <div>
1451            <p>Vohs John Doe</p>
1452          </div>
1453        </html>""", tmpl.generate(two=2, fname='John', lname='Doe').render())
1454       
1455    def test_translate_i18n_choose_and_singular_with_py_strip(self):
1456        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1457            xmlns:i18n="http://genshi.edgewall.org/i18n">
1458          <div i18n:choose="two; fname, lname">
1459            <p i18n:singular="" py:strip="">Foo $fname $lname</p>
1460            <p i18n:plural="">Foos $fname $lname</p>
1461          </div>
1462          <div i18n:choose="one; fname, lname">
1463            <p i18n:singular="" py:strip="">Foo $fname $lname</p>
1464            <p i18n:plural="">Foos $fname $lname</p>
1465          </div>
1466        </html>""")
1467        translations = DummyTranslations({
1468            ('Foo %(fname)s %(lname)s', 0): 'Voh %(fname)s %(lname)s',
1469            ('Foo %(fname)s %(lname)s', 1): 'Vohs %(fname)s %(lname)s',
1470                 'Foo %(fname)s %(lname)s': 'Voh %(fname)s %(lname)s',
1471                'Foos %(fname)s %(lname)s': 'Vohs %(fname)s %(lname)s',
1472        })
1473        translator = Translator(translations)
1474        translator.setup(tmpl)
1475        self.assertEqual("""<html>
1476          <div>
1477            <p>Vohs John Doe</p>
1478          </div>
1479          <div>
1480            Voh John Doe
1481          </div>
1482        </html>""", tmpl.generate(
1483            one=1, two=2, fname='John',lname='Doe').render())
1484       
1485    def test_translate_i18n_choose_and_plural_with_py_strip(self):
1486        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1487            xmlns:i18n="http://genshi.edgewall.org/i18n">
1488          <div i18n:choose="two; fname, lname">
1489            <p i18n:singular="" py:strip="">Foo $fname $lname</p>
1490            <p i18n:plural="">Foos $fname $lname</p>
1491          </div>
1492        </html>""")
1493        translations = DummyTranslations({
1494            ('Foo %(fname)s %(lname)s', 0): 'Voh %(fname)s %(lname)s',
1495            ('Foo %(fname)s %(lname)s', 1): 'Vohs %(fname)s %(lname)s',
1496                 'Foo %(fname)s %(lname)s': 'Voh %(fname)s %(lname)s',
1497                'Foos %(fname)s %(lname)s': 'Vohs %(fname)s %(lname)s',
1498        })
1499        translator = Translator(translations)
1500        translator.setup(tmpl)
1501        self.assertEqual("""<html>
1502          <div>
1503            Voh John Doe
1504          </div>
1505        </html>""", tmpl.generate(two=1, fname='John', lname='Doe').render())
1506
1507    def test_extract_i18n_choose_as_attribute_and_py_strip(self):
1508        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1509            xmlns:i18n="http://genshi.edgewall.org/i18n">
1510          <div i18n:choose="one" py:strip="">
1511            <p i18n:singular="" py:strip="">FooBar</p>
1512            <p i18n:plural="" py:strip="">FooBars</p>
1513          </div>
1514        </html>""")
1515        translator = Translator()
1516        tmpl.add_directives(Translator.NAMESPACE, translator)
1517        messages = list(translator.extract(tmpl.stream))
1518        self.assertEqual(1, len(messages))
1519        self.assertEqual((3, 'ngettext', ('FooBar', 'FooBars'), []), messages[0])
1520
1521
1522class DomainDirectiveTestCase(unittest.TestCase):
1523
1524    def test_translate_i18n_domain_with_msg_directives(self):
1525        #"""translate with i18n:domain and nested i18n:msg directives """
1526
1527        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1528            xmlns:i18n="http://genshi.edgewall.org/i18n">
1529          <div i18n:domain="foo">
1530            <p i18n:msg="">FooBar</p>
1531            <p i18n:msg="">Bar</p>
1532          </div>
1533        </html>""")
1534        translations = DummyTranslations({'Bar': 'Voh'})
1535        translations.add_domain('foo', {'FooBar': 'BarFoo', 'Bar': 'PT_Foo'})
1536        translator = Translator(translations)
1537        translator.setup(tmpl)
1538        self.assertEqual("""<html>
1539          <div>
1540            <p>BarFoo</p>
1541            <p>PT_Foo</p>
1542          </div>
1543        </html>""", tmpl.generate().render())
1544
1545    def test_translate_i18n_domain_with_inline_directives(self):
1546        #"""translate with inlined i18n:domain and i18n:msg directives"""
1547        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1548            xmlns:i18n="http://genshi.edgewall.org/i18n">
1549          <p i18n:msg="" i18n:domain="foo">FooBar</p>
1550        </html>""")
1551        translations = DummyTranslations({'Bar': 'Voh'})
1552        translations.add_domain('foo', {'FooBar': 'BarFoo'})
1553        translator = Translator(translations)
1554        translator.setup(tmpl)
1555        self.assertEqual("""<html>
1556          <p>BarFoo</p>
1557        </html>""", tmpl.generate().render())
1558
1559    def test_translate_i18n_domain_without_msg_directives(self):
1560        #"""translate domain call without i18n:msg directives still uses current domain"""
1561
1562        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1563            xmlns:i18n="http://genshi.edgewall.org/i18n">
1564          <p i18n:msg="">Bar</p>
1565          <div i18n:domain="foo">
1566            <p i18n:msg="">FooBar</p>
1567            <p i18n:msg="">Bar</p>
1568            <p>Bar</p>
1569          </div>
1570          <p>Bar</p>
1571        </html>""")
1572        translations = DummyTranslations({'Bar': 'Voh'})
1573        translations.add_domain('foo', {'FooBar': 'BarFoo', 'Bar': 'PT_Foo'})
1574        translator = Translator(translations)
1575        translator.setup(tmpl)
1576        self.assertEqual("""<html>
1577          <p>Voh</p>
1578          <div>
1579            <p>BarFoo</p>
1580            <p>PT_Foo</p>
1581            <p>PT_Foo</p>
1582          </div>
1583          <p>Voh</p>
1584        </html>""", tmpl.generate().render())
1585
1586    def test_translate_i18n_domain_as_directive_not_attribute(self):
1587        #"""translate with domain as directive"""
1588
1589        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1590            xmlns:i18n="http://genshi.edgewall.org/i18n">
1591        <i18n:domain name="foo">
1592          <p i18n:msg="">FooBar</p>
1593          <p i18n:msg="">Bar</p>
1594          <p>Bar</p>
1595        </i18n:domain>
1596          <p>Bar</p>
1597        </html>""")
1598        translations = DummyTranslations({'Bar': 'Voh'})
1599        translations.add_domain('foo', {'FooBar': 'BarFoo', 'Bar': 'PT_Foo'})
1600        translator = Translator(translations)
1601        translator.setup(tmpl)
1602        self.assertEqual("""<html>
1603          <p>BarFoo</p>
1604          <p>PT_Foo</p>
1605          <p>PT_Foo</p>
1606          <p>Voh</p>
1607        </html>""", tmpl.generate().render())
1608
1609    def test_translate_i18n_domain_nested_directives(self):
1610        #"""translate with nested i18n:domain directives"""
1611
1612        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1613            xmlns:i18n="http://genshi.edgewall.org/i18n">
1614          <p i18n:msg="">Bar</p>
1615          <div i18n:domain="foo">
1616            <p i18n:msg="">FooBar</p>
1617            <p i18n:domain="bar" i18n:msg="">Bar</p>
1618            <p>Bar</p>
1619          </div>
1620          <p>Bar</p>
1621        </html>""")
1622        translations = DummyTranslations({'Bar': 'Voh'})
1623        translations.add_domain('foo', {'FooBar': 'BarFoo', 'Bar': 'foo_Bar'})
1624        translations.add_domain('bar', {'Bar': 'bar_Bar'})
1625        translator = Translator(translations)
1626        translator.setup(tmpl)
1627        self.assertEqual("""<html>
1628          <p>Voh</p>
1629          <div>
1630            <p>BarFoo</p>
1631            <p>bar_Bar</p>
1632            <p>foo_Bar</p>
1633          </div>
1634          <p>Voh</p>
1635        </html>""", tmpl.generate().render())
1636
1637    def test_translate_i18n_domain_with_empty_nested_domain_directive(self):
1638        #"""translate with empty nested i18n:domain directive does not use dngettext"""
1639
1640        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1641            xmlns:i18n="http://genshi.edgewall.org/i18n">
1642          <p i18n:msg="">Bar</p>
1643          <div i18n:domain="foo">
1644            <p i18n:msg="">FooBar</p>
1645            <p i18n:domain="" i18n:msg="">Bar</p>
1646            <p>Bar</p>
1647          </div>
1648          <p>Bar</p>
1649        </html>""")
1650        translations = DummyTranslations({'Bar': 'Voh'})
1651        translations.add_domain('foo', {'FooBar': 'BarFoo', 'Bar': 'foo_Bar'})
1652        translations.add_domain('bar', {'Bar': 'bar_Bar'})
1653        translator = Translator(translations)
1654        translator.setup(tmpl)
1655        self.assertEqual("""<html>
1656          <p>Voh</p>
1657          <div>
1658            <p>BarFoo</p>
1659            <p>Voh</p>
1660            <p>foo_Bar</p>
1661          </div>
1662          <p>Voh</p>
1663        </html>""", tmpl.generate().render())
1664
1665    def test_translate_i18n_domain_with_inline_directive_on_START_NS(self):
1666        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1667            xmlns:i18n="http://genshi.edgewall.org/i18n" i18n:domain="foo">
1668          <p i18n:msg="">FooBar</p>
1669        </html>""")
1670        translations = DummyTranslations({'Bar': 'Voh'})
1671        translations.add_domain('foo', {'FooBar': 'BarFoo'})
1672        translator = Translator(translations)
1673        translator.setup(tmpl)
1674        self.assertEqual("""<html>
1675          <p>BarFoo</p>
1676        </html>""", tmpl.generate().render())
1677
1678    def test_translate_i18n_domain_with_inline_directive_on_START_NS_with_py_strip(self):
1679        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
1680            xmlns:i18n="http://genshi.edgewall.org/i18n"
1681            i18n:domain="foo" py:strip="">
1682          <p i18n:msg="">FooBar</p>
1683        </html>""")
1684        translations = DummyTranslations({'Bar': 'Voh'})
1685        translations.add_domain('foo', {'FooBar': 'BarFoo'})
1686        translator = Translator(translations)
1687        translator.setup(tmpl)
1688        self.assertEqual("""
1689          <p>BarFoo</p>
1690        """, tmpl.generate().render())
1691
1692    def test_translate_i18n_domain_with_nested_includes(self):
1693        import os, shutil, tempfile
1694        from genshi.template.loader import TemplateLoader
1695        dirname = tempfile.mkdtemp(suffix='genshi_test')
1696        try:
1697            for idx in range(7):
1698                file1 = open(os.path.join(dirname, 'tmpl%d.html' % idx), 'w')
1699                try:
1700                    file1.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude"
1701                                         xmlns:py="http://genshi.edgewall.org/"
1702                                         xmlns:i18n="http://genshi.edgewall.org/i18n" py:strip="">
1703                        <div>Included tmpl$idx</div>
1704                        <p i18n:msg="idx">Bar $idx</p>
1705                        <p i18n:domain="bar">Bar</p>
1706                        <p i18n:msg="idx" i18n:domain="">Bar $idx</p>
1707                        <p i18n:domain="" i18n:msg="idx">Bar $idx</p>
1708                        <py:if test="idx &lt; 6">
1709                        <xi:include href="tmpl${idx}.html" py:with="idx = idx+1"/>
1710                        </py:if>
1711                    </html>""")
1712                finally:
1713                    file1.close()
1714
1715            file2 = open(os.path.join(dirname, 'tmpl10.html'), 'w')
1716            try:
1717                file2.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude"
1718                                     xmlns:py="http://genshi.edgewall.org/"
1719                                     xmlns:i18n="http://genshi.edgewall.org/i18n"
1720                                     i18n:domain="foo">
1721                  <xi:include href="tmpl${idx}.html" py:with="idx = idx+1"/>
1722                </html>""")
1723            finally:
1724                file2.close()
1725
1726            def callback(template):
1727                translations = DummyTranslations({'Bar %(idx)s': 'Voh %(idx)s'})
1728                translations.add_domain('foo', {'Bar %(idx)s': 'foo_Bar %(idx)s'})
1729                translations.add_domain('bar', {'Bar': 'bar_Bar'})
1730                translator = Translator(translations)
1731                translator.setup(template)
1732            loader = TemplateLoader([dirname], callback=callback)
1733            tmpl = loader.load('tmpl10.html')
1734
1735            self.assertEqual("""<html>
1736                        <div>Included tmpl0</div>
1737                        <p>foo_Bar 0</p>
1738                        <p>bar_Bar</p>
1739                        <p>Voh 0</p>
1740                        <p>Voh 0</p>
1741                        <div>Included tmpl1</div>
1742                        <p>foo_Bar 1</p>
1743                        <p>bar_Bar</p>
1744                        <p>Voh 1</p>
1745                        <p>Voh 1</p>
1746                        <div>Included tmpl2</div>
1747                        <p>foo_Bar 2</p>
1748                        <p>bar_Bar</p>
1749                        <p>Voh 2</p>
1750                        <p>Voh 2</p>
1751                        <div>Included tmpl3</div>
1752                        <p>foo_Bar 3</p>
1753                        <p>bar_Bar</p>
1754                        <p>Voh 3</p>
1755                        <p>Voh 3</p>
1756                        <div>Included tmpl4</div>
1757                        <p>foo_Bar 4</p>
1758                        <p>bar_Bar</p>
1759                        <p>Voh 4</p>
1760                        <p>Voh 4</p>
1761                        <div>Included tmpl5</div>
1762                        <p>foo_Bar 5</p>
1763                        <p>bar_Bar</p>
1764                        <p>Voh 5</p>
1765                        <p>Voh 5</p>
1766                        <div>Included tmpl6</div>
1767                        <p>foo_Bar 6</p>
1768                        <p>bar_Bar</p>
1769                        <p>Voh 6</p>
1770                        <p>Voh 6</p>
1771                </html>""", tmpl.generate(idx=-1).render())
1772        finally:
1773            shutil.rmtree(dirname)
1774
1775    def test_translate_i18n_domain_with_nested_includes_with_translatable_attrs(self):
1776        import os, shutil, tempfile
1777        from genshi.template.loader import TemplateLoader
1778        dirname = tempfile.mkdtemp(suffix='genshi_test')
1779        try:
1780            for idx in range(4):
1781                file1 = open(os.path.join(dirname, 'tmpl%d.html' % idx), 'w')
1782                try:
1783                    file1.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude"
1784                                         xmlns:py="http://genshi.edgewall.org/"
1785                                         xmlns:i18n="http://genshi.edgewall.org/i18n" py:strip="">
1786                        <div>Included tmpl$idx</div>
1787                        <p title="${dg('foo', 'Bar %(idx)s') % dict(idx=idx)}" i18n:msg="idx">Bar $idx</p>
1788                        <p title="Bar" i18n:domain="bar">Bar</p>
1789                        <p title="Bar" i18n:msg="idx" i18n:domain="">Bar $idx</p>
1790                        <p i18n:msg="idx" i18n:domain="" title="Bar">Bar $idx</p>
1791                        <p i18n:domain="" i18n:msg="idx" title="Bar">Bar $idx</p>
1792                        <py:if test="idx &lt; 3">
1793                        <xi:include href="tmpl${idx}.html" py:with="idx = idx+1"/>
1794                        </py:if>
1795                    </html>""")
1796                finally:
1797                    file1.close()
1798
1799            file2 = open(os.path.join(dirname, 'tmpl10.html'), 'w')
1800            try:
1801                file2.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude"
1802                                     xmlns:py="http://genshi.edgewall.org/"
1803                                     xmlns:i18n="http://genshi.edgewall.org/i18n"
1804                                     i18n:domain="foo">
1805                  <xi:include href="tmpl${idx}.html" py:with="idx = idx+1"/>
1806                </html>""")
1807            finally:
1808                file2.close()
1809
1810            translations = DummyTranslations({'Bar %(idx)s': 'Voh %(idx)s',
1811                                              'Bar': 'Voh'})
1812            translations.add_domain('foo', {'Bar %(idx)s': 'foo_Bar %(idx)s'})
1813            translations.add_domain('bar', {'Bar': 'bar_Bar'})
1814            translator = Translator(translations)
1815
1816            def callback(template):
1817                translator.setup(template)
1818            loader = TemplateLoader([dirname], callback=callback)
1819            tmpl = loader.load('tmpl10.html')
1820
1821            if IS_PYTHON2:
1822                dgettext = translations.dugettext
1823            else:
1824                dgettext = translations.dgettext
1825
1826            self.assertEqual("""<html>
1827                        <div>Included tmpl0</div>
1828                        <p title="foo_Bar 0">foo_Bar 0</p>
1829                        <p title="bar_Bar">bar_Bar</p>
1830                        <p title="Voh">Voh 0</p>
1831                        <p title="Voh">Voh 0</p>
1832                        <p title="Voh">Voh 0</p>
1833                        <div>Included tmpl1</div>
1834                        <p title="foo_Bar 1">foo_Bar 1</p>
1835                        <p title="bar_Bar">bar_Bar</p>
1836                        <p title="Voh">Voh 1</p>
1837                        <p title="Voh">Voh 1</p>
1838                        <p title="Voh">Voh 1</p>
1839                        <div>Included tmpl2</div>
1840                        <p title="foo_Bar 2">foo_Bar 2</p>
1841                        <p title="bar_Bar">bar_Bar</p>
1842                        <p title="Voh">Voh 2</p>
1843                        <p title="Voh">Voh 2</p>
1844                        <p title="Voh">Voh 2</p>
1845                        <div>Included tmpl3</div>
1846                        <p title="foo_Bar 3">foo_Bar 3</p>
1847                        <p title="bar_Bar">bar_Bar</p>
1848                        <p title="Voh">Voh 3</p>
1849                        <p title="Voh">Voh 3</p>
1850                        <p title="Voh">Voh 3</p>
1851                </html>""", tmpl.generate(idx=-1,
1852                                          dg=dgettext).render())
1853        finally:
1854            shutil.rmtree(dirname)
1855
1856
1857class ExtractTestCase(unittest.TestCase):
1858
1859    def test_markup_template_extraction(self):
1860        buf = StringIO("""<html xmlns:py="http://genshi.edgewall.org/">
1861          <head>
1862            <title>Example</title>
1863          </head>
1864          <body>
1865            <h1>Example</h1>
1866            <p>${_("Hello, %(name)s") % dict(name=username)}</p>
1867            <p>${ngettext("You have %d item", "You have %d items", num)}</p>
1868          </body>
1869        </html>""")
1870        results = list(extract(buf, ['_', 'ngettext'], [], {}))
1871        self.assertEqual([
1872            (3, None, 'Example', []),
1873            (6, None, 'Example', []),
1874            (7, '_', 'Hello, %(name)s', []),
1875            (8, 'ngettext', ('You have %d item', 'You have %d items', None),
1876                             []),
1877        ], results)
1878
1879    def test_extraction_without_text(self):
1880        buf = StringIO("""<html xmlns:py="http://genshi.edgewall.org/">
1881          <p title="Bar">Foo</p>
1882          ${ngettext("Singular", "Plural", num)}
1883        </html>""")
1884        results = list(extract(buf, ['_', 'ngettext'], [], {
1885            'extract_text': 'no'
1886        }))
1887        self.assertEqual([
1888            (3, 'ngettext', ('Singular', 'Plural', None), []),
1889        ], results)
1890
1891    def test_text_template_extraction(self):
1892        buf = StringIO("""${_("Dear %(name)s") % {'name': name}},
1893
1894        ${ngettext("Your item:", "Your items", len(items))}
1895        #for item in items
1896         * $item
1897        #end
1898
1899        All the best,
1900        Foobar""")
1901        results = list(extract(buf, ['_', 'ngettext'], [], {
1902            'template_class': 'genshi.template:TextTemplate'
1903        }))
1904        self.assertEqual([
1905            (1, '_', 'Dear %(name)s', []),
1906            (3, 'ngettext', ('Your item:', 'Your items', None), []),
1907            (7, None, 'All the best,\n        Foobar', [])
1908        ], results)
1909
1910    def test_extraction_with_keyword_arg(self):
1911        buf = StringIO("""<html xmlns:py="http://genshi.edgewall.org/">
1912          ${gettext('Foobar', foo='bar')}
1913        </html>""")
1914        results = list(extract(buf, ['gettext'], [], {}))
1915        self.assertEqual([
1916            (2, 'gettext', ('Foobar'), []),
1917        ], results)
1918
1919    def test_extraction_with_nonstring_arg(self):
1920        buf = StringIO("""<html xmlns:py="http://genshi.edgewall.org/">
1921          ${dgettext(curdomain, 'Foobar')}
1922        </html>""")
1923        results = list(extract(buf, ['dgettext'], [], {}))
1924        self.assertEqual([
1925            (2, 'dgettext', (None, 'Foobar'), []),
1926        ], results)
1927
1928    def test_extraction_inside_ignored_tags(self):
1929        buf = StringIO("""<html xmlns:py="http://genshi.edgewall.org/">
1930          <script type="text/javascript">
1931            $('#llist').tabs({
1932              remote: true,
1933              spinner: "${_('Please wait...')}"
1934            });
1935          </script>
1936        </html>""")
1937        results = list(extract(buf, ['_'], [], {}))
1938        self.assertEqual([
1939            (5, '_', 'Please wait...', []),
1940        ], results)
1941
1942    def test_extraction_inside_ignored_tags_with_directives(self):
1943        buf = StringIO("""<html xmlns:py="http://genshi.edgewall.org/">
1944          <script type="text/javascript">
1945            <py:if test="foobar">
1946              alert("This shouldn't be extracted");
1947            </py:if>
1948          </script>
1949        </html>""")
1950        self.assertEqual([], list(extract(buf, ['_'], [], {})))
1951
1952    def test_extract_py_def_directive_with_py_strip(self):
1953        # Failed extraction from Trac
1954        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" py:strip="">
1955    <py:def function="diff_options_fields(diff)">
1956    <label for="style">View differences</label>
1957    <select id="style" name="style">
1958      <option selected="${diff.style == 'inline' or None}"
1959              value="inline">inline</option>
1960      <option selected="${diff.style == 'sidebyside' or None}"
1961              value="sidebyside">side by side</option>
1962    </select>
1963    <div class="field">
1964      Show <input type="text" name="contextlines" id="contextlines" size="2"
1965                  maxlength="3" value="${diff.options.contextlines &lt; 0 and 'all' or diff.options.contextlines}" />
1966      <label for="contextlines">lines around each change</label>
1967    </div>
1968    <fieldset id="ignore" py:with="options = diff.options">
1969      <legend>Ignore:</legend>
1970      <div class="field">
1971        <input type="checkbox" id="ignoreblanklines" name="ignoreblanklines"
1972               checked="${options.ignoreblanklines or None}" />
1973        <label for="ignoreblanklines">Blank lines</label>
1974      </div>
1975      <div class="field">
1976        <input type="checkbox" id="ignorecase" name="ignorecase"
1977               checked="${options.ignorecase or None}" />
1978        <label for="ignorecase">Case changes</label>
1979      </div>
1980      <div class="field">
1981        <input type="checkbox" id="ignorewhitespace" name="ignorewhitespace"
1982               checked="${options.ignorewhitespace or None}" />
1983        <label for="ignorewhitespace">White space changes</label>
1984      </div>
1985    </fieldset>
1986    <div class="buttons">
1987      <input type="submit" name="update" value="${_('Update')}" />
1988    </div>
1989  </py:def></html>""")
1990        translator = Translator()
1991        tmpl.add_directives(Translator.NAMESPACE, translator)
1992        messages = list(translator.extract(tmpl.stream))
1993        self.assertEqual(10, len(messages))
1994        self.assertEqual([
1995            (3, None, 'View differences', []),
1996            (6, None, 'inline', []),
1997            (8, None, 'side by side', []),
1998            (10, None, 'Show', []),
1999            (13, None, 'lines around each change', []),
2000            (16, None, 'Ignore:', []),
2001            (20, None, 'Blank lines', []),
2002            (25, None, 'Case changes',[]),
2003            (30, None, 'White space changes', []),
2004            (34, '_', 'Update', [])], messages)
2005
2006
2007def suite():
2008    suite = unittest.TestSuite()
2009    suite.addTest(doctest.DocTestSuite(Translator.__module__))
2010    suite.addTest(unittest.makeSuite(TranslatorTestCase, 'test'))
2011    suite.addTest(unittest.makeSuite(MsgDirectiveTestCase, 'test'))
2012    suite.addTest(unittest.makeSuite(ChooseDirectiveTestCase, 'test'))
2013    suite.addTest(unittest.makeSuite(DomainDirectiveTestCase, 'test'))
2014    suite.addTest(unittest.makeSuite(ExtractTestCase, 'test'))
2015    return suite
2016
2017if __name__ == '__main__':
2018    unittest.main(defaultTest='suite')
Note: See TracBrowser for help on using the repository browser.