diff genshi/filters/tests/i18n.py @ 849:878c4313c7d5 trunk

Merged advanced-i18n branch back into trunk.
author cmlenz
date Tue, 10 Nov 2009 20:54:06 +0000
parents da90cee22560
children e8f596c726c4
line wrap: on
line diff
--- a/genshi/filters/tests/i18n.py
+++ b/genshi/filters/tests/i18n.py
@@ -18,16 +18,26 @@
 import unittest
 
 from genshi.core import Attrs
-from genshi.template import MarkupTemplate
+from genshi.template import MarkupTemplate, Context
 from genshi.filters.i18n import Translator, extract
 from genshi.input import HTML
 
 
 class DummyTranslations(NullTranslations):
+    _domains = {}
 
-    def __init__(self, catalog):
+    def __init__(self, catalog=()):
         NullTranslations.__init__(self)
-        self._catalog = catalog
+        self._catalog = catalog or {}
+        self.plural = lambda n: n != 1
+
+    def add_domain(self, domain, catalog):
+        translation = DummyTranslations(catalog)
+        translation.add_fallback(self)
+        self._domains[domain] = translation
+
+    def _domain_call(self, func, domain, *args, **kwargs):
+        return getattr(self._domains.get(domain, self), func)(*args, **kwargs)
 
     def ugettext(self, message):
         missing = object()
@@ -38,6 +48,23 @@
             return unicode(message)
         return tmsg
 
+    def dugettext(self, domain, message):
+        return self._domain_call('ugettext', domain, message)
+
+    def ungettext(self, msgid1, msgid2, n):
+        try:
+            return self._catalog[(msgid1, self.plural(n))]
+        except KeyError:
+            if self._fallback:
+                return self._fallback.ngettext(msgid1, msgid2, n)
+            if n == 1:
+                return msgid1
+            else:
+                return msgid2
+
+    def dungettext(self, domain, singular, plural, numeral):
+        return self._domain_call('ungettext', domain, singular, plural, numeral)
+
 
 class TranslatorTestCase(unittest.TestCase):
 
@@ -162,6 +189,7 @@
           </p>
         </html>""")
         translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
         messages = list(translator.extract(tmpl.stream))
         self.assertEqual(1, len(messages))
         self.assertEqual('Please see [1:Help] for details.', messages[0][2])
@@ -175,8 +203,7 @@
         </html>""")
         gettext = lambda s: u"Für Details siehe bitte [1:Hilfe]."
         translator = Translator(gettext)
-        tmpl.filters.insert(0, translator)
-        tmpl.add_directives(Translator.NAMESPACE, translator)
+        translator.setup(tmpl)
         self.assertEqual("""<html>
           <p>Für Details siehe bitte <a href="help.html">Hilfe</a>.</p>
         </html>""", tmpl.generate().render())
@@ -189,6 +216,7 @@
           </p>
         </html>""")
         translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
         messages = list(translator.extract(tmpl.stream))
         self.assertEqual(1, len(messages))
         self.assertEqual('Please see [1:[2:Help] page] for details.',
@@ -203,12 +231,39 @@
         </html>""")
         gettext = lambda s: u"Für Details siehe bitte [1:[2:Hilfeseite]]."
         translator = Translator(gettext)
-        tmpl.filters.insert(0, translator)
-        tmpl.add_directives(Translator.NAMESPACE, translator)
+        translator.setup(tmpl)
         self.assertEqual("""<html>
           <p>Für Details siehe bitte <a href="help.html"><em>Hilfeseite</em></a>.</p>
         </html>""", tmpl.generate().render())
 
+    def test_extract_i18n_msg_label_with_nested_input(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <div i18n:msg="">
+            <label><input type="text" size="3" name="daysback" value="30" /> days back</label>
+          </div>
+        </html>""")
+        translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
+        messages = list(translator.extract(tmpl.stream))
+        self.assertEqual(1, len(messages))
+        self.assertEqual('[1:[2:] days back]',
+                         messages[0][2])
+
+    def test_translate_i18n_msg_label_with_nested_input(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <div i18n:msg="">
+            <label><input type="text" size="3" name="daysback" value="30" /> foo bar</label>
+          </div>
+        </html>""")
+        gettext = lambda s: "[1:[2:] foo bar]"
+        translator = Translator(gettext)
+        translator.setup(tmpl)
+        self.assertEqual("""<html>
+          <div><label><input type="text" size="3" name="daysback" value="30"/> foo bar</label></div>
+        </html>""", tmpl.generate().render())
+
     def test_extract_i18n_msg_empty(self):
         tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
             xmlns:i18n="http://genshi.edgewall.org/i18n">
@@ -217,6 +272,7 @@
           </p>
         </html>""")
         translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
         messages = list(translator.extract(tmpl.stream))
         self.assertEqual(1, len(messages))
         self.assertEqual('Show me [1:] entries per page.', messages[0][2])
@@ -230,8 +286,7 @@
         </html>""")
         gettext = lambda s: u"[1:] Einträge pro Seite anzeigen."
         translator = Translator(gettext)
-        tmpl.filters.insert(0, translator)
-        tmpl.add_directives(Translator.NAMESPACE, translator)
+        translator.setup(tmpl)
         self.assertEqual("""<html>
           <p><input type="text" name="num"/> Einträge pro Seite anzeigen.</p>
         </html>""", tmpl.generate().render())
@@ -244,6 +299,7 @@
           </p>
         </html>""")
         translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
         messages = list(translator.extract(tmpl.stream))
         self.assertEqual(1, len(messages))
         self.assertEqual('Please see [1:Help] for [2:details].', messages[0][2])
@@ -257,8 +313,7 @@
         </html>""")
         gettext = lambda s: u"Für [2:Details] siehe bitte [1:Hilfe]."
         translator = Translator(gettext)
-        tmpl.filters.insert(0, translator)
-        tmpl.add_directives(Translator.NAMESPACE, translator)
+        translator.setup(tmpl)
         self.assertEqual("""<html>
           <p>Für <em>Details</em> siehe bitte <a href="help.html">Hilfe</a>.</p>
         </html>""", tmpl.generate().render())
@@ -271,6 +326,7 @@
           </p>
         </html>""")
         translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
         messages = list(translator.extract(tmpl.stream))
         self.assertEqual(1, len(messages))
         self.assertEqual('Show me [1:] entries per page, starting at page [2:].',
@@ -285,8 +341,7 @@
         </html>""")
         gettext = lambda s: u"[1:] Einträge pro Seite, beginnend auf Seite [2:]."
         translator = Translator(gettext)
-        tmpl.filters.insert(0, translator)
-        tmpl.add_directives(Translator.NAMESPACE, translator)
+        translator.setup(tmpl)
         self.assertEqual("""<html>
           <p><input type="text" name="num"/> Eintr\xc3\xa4ge pro Seite, beginnend auf Seite <input type="text" name="num"/>.</p>
         </html>""", tmpl.generate().render())
@@ -299,6 +354,7 @@
           </p>
         </html>""")
         translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
         messages = list(translator.extract(tmpl.stream))
         self.assertEqual(1, len(messages))
         self.assertEqual('Hello, %(name)s!', messages[0][2])
@@ -312,8 +368,7 @@
         </html>""")
         gettext = lambda s: u"Hallo, %(name)s!"
         translator = Translator(gettext)
-        tmpl.filters.insert(0, translator)
-        tmpl.add_directives(Translator.NAMESPACE, translator)
+        translator.setup(tmpl)
         self.assertEqual("""<html>
           <p>Hallo, Jim!</p>
         </html>""", tmpl.generate(user=dict(name='Jim')).render())
@@ -327,8 +382,7 @@
         </html>""")
         gettext = lambda s: u"%(name)s, sei gegrüßt!"
         translator = Translator(gettext)
-        tmpl.filters.insert(0, translator)
-        tmpl.add_directives(Translator.NAMESPACE, translator)
+        translator.setup(tmpl)
         self.assertEqual("""<html>
           <p>Jim, sei gegrüßt!</p>
         </html>""", tmpl.generate(user=dict(name='Jim')).render())
@@ -342,8 +396,7 @@
         </html>""")
         gettext = lambda s: u"Sei gegrüßt, [1:Alter]!"
         translator = Translator(gettext)
-        tmpl.filters.insert(0, translator)
-        tmpl.add_directives(Translator.NAMESPACE, translator)
+        translator.setup(tmpl)
         self.assertEqual("""<html>
           <p>Sei gegrüßt, <a href="#42">Alter</a>!</p>
         </html>""", tmpl.generate(anchor='42').render())
@@ -356,6 +409,7 @@
           </p>
         </html>""")
         translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
         messages = list(translator.extract(tmpl.stream))
         self.assertEqual(1, len(messages))
         self.assertEqual('Posted by %(name)s at %(time)s', messages[0][2])
@@ -369,8 +423,7 @@
         </html>""")
         gettext = lambda s: u"%(name)s schrieb dies um %(time)s"
         translator = Translator(gettext)
-        tmpl.filters.insert(0, translator)
-        tmpl.add_directives(Translator.NAMESPACE, translator)
+        translator.setup(tmpl)
         entry = {
             'author': 'Jim',
             'time': datetime(2008, 4, 1, 14, 30)
@@ -387,30 +440,41 @@
           </p>
         </html>""")
         translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
         messages = list(translator.extract(tmpl.stream))
         self.assertEqual(1, len(messages))
         self.assertEqual('Show me [1:] entries per page.', messages[0][2])
 
-    # FIXME: this currently fails :-/
-#    def test_translate_i18n_msg_with_directive(self):
-#        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
-#            xmlns:i18n="http://genshi.edgewall.org/i18n">
-#          <p i18n:msg="">
-#            Show me <input type="text" name="num" py:attrs="{'value': x}" /> entries per page.
-#          </p>
-#        </html>""")
-#        gettext = lambda s: u"[1:] Einträge pro Seite anzeigen."
-#        tmpl.filters.insert(0, Translator(gettext))
-#        self.assertEqual("""<html>
-#          <p><input type="text" name="num" value="x"/> Einträge pro Seite anzeigen.</p>
-#        </html>""", tmpl.generate().render())
+    def test_translate_i18n_msg_with_directive(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <p i18n:msg="">
+            Show me <input type="text" name="num" py:attrs="{'value': 'x'}" /> entries per page.
+          </p>
+        </html>""")
+        gettext = lambda s: u"[1:] Einträge pro Seite anzeigen."
+        translator = Translator(gettext)
+        translator.setup(tmpl)
+        self.assertEqual("""<html>
+          <p><input type="text" name="num" value="x"/> Einträge pro Seite anzeigen.</p>
+        </html>""", tmpl.generate().render())
 
     def test_extract_i18n_msg_with_comment(self):
         tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
             xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <p i18n:comment="As in foo bar" i18n:msg="">Foo</p>
+        </html>""")
+        translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
+        messages = list(translator.extract(tmpl.stream))
+        self.assertEqual(1, len(messages))
+        self.assertEqual((3, None, u'Foo', ['As in foo bar']), messages[0])
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
           <p i18n:msg="" i18n:comment="As in foo bar">Foo</p>
         </html>""")
         translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
         messages = list(translator.extract(tmpl.stream))
         self.assertEqual(1, len(messages))
         self.assertEqual((3, None, u'Foo', ['As in foo bar']), messages[0])
@@ -422,8 +486,7 @@
         </html>""")
         gettext = lambda s: u"Voh"
         translator = Translator(gettext)
-        tmpl.filters.insert(0, translator)
-        tmpl.add_directives(Translator.NAMESPACE, translator)
+        translator.setup(tmpl)
         self.assertEqual("""<html>
           <p>Voh</p>
         </html>""", tmpl.generate().render())
@@ -461,12 +524,908 @@
           <p i18n:msg="" i18n:comment="As in foo bar">Foo</p>
         </html>""")
         translator = Translator(DummyTranslations({'Foo': 'Voh'}))
-        tmpl.filters.insert(0, translator)
-        tmpl.add_directives(Translator.NAMESPACE, translator)
+        translator.setup(tmpl)
         self.assertEqual("""<html>
           <p>Voh</p>
         </html>""", tmpl.generate().render())
 
+    def test_translate_i18n_msg_and_py_strip_directives(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <p i18n:msg="" py:strip="">Foo</p>
+          <p py:strip="" i18n:msg="">Foo</p>
+        </html>""")
+        translator = Translator(DummyTranslations({'Foo': 'Voh'}))
+        translator.setup(tmpl)
+        self.assertEqual("""<html>
+          Voh
+          Voh
+        </html>""", tmpl.generate().render())
+
+    def test_i18n_msg_ticket_300_extract(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <i18n:msg params="date, author">
+            Changed ${ '10/12/2008' } ago by ${ 'me, the author' }
+          </i18n:msg>
+        </html>""")
+        translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
+        messages = list(translator.extract(tmpl.stream))
+        self.assertEqual(1, len(messages))
+        self.assertEqual(
+            (3, None, u'Changed %(date)s ago by %(author)s', []), messages[0]
+        )
+
+    def test_i18n_msg_ticket_300_translate(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <i18n:msg params="date, author">
+            Changed ${ date } ago by ${ author }
+          </i18n:msg>
+        </html>""")
+        translations = DummyTranslations({
+            u'Changed %(date)s ago by %(author)s': u'Modificado à %(date)s por %(author)s'
+        })
+        translator = Translator(translations)
+        translator.setup(tmpl)
+        self.assertEqual("""<html>
+          Modificado à um dia por Pedro
+        </html>""", tmpl.generate(date='um dia', author="Pedro").render())
+
+
+    def test_i18n_msg_ticket_251_extract(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <p i18n:msg=""><tt><b>Translation[&nbsp;0&nbsp;]</b>: <em>One coin</em></tt></p>
+        </html>""")
+        translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
+        messages = list(translator.extract(tmpl.stream))
+        self.assertEqual(1, len(messages))
+        self.assertEqual(
+            (3, None, u'[1:[2:Translation\\[\xa00\xa0\\]]: [3:One coin]]', []), messages[0]
+        )
+
+    def test_i18n_msg_ticket_251_translate(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <p i18n:msg=""><tt><b>Translation[&nbsp;0&nbsp;]</b>: <em>One coin</em></tt></p>
+        </html>""")
+        translations = DummyTranslations({
+            u'[1:[2:Translation\\[\xa00\xa0\\]]: [3:One coin]]':
+                u'[1:[2:Trandução\\[\xa00\xa0\\]]: [3:Uma moeda]]'
+        })
+        translator = Translator(translations)
+        translator.setup(tmpl)
+        self.assertEqual("""<html>
+          <p><tt><b>Trandução[ 0 ]</b>: <em>Uma moeda</em></tt></p>
+        </html>""", tmpl.generate().render())
+
+    def test_extract_i18n_msg_with_other_directives_nested(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <p i18n:msg="" py:with="q = quote_plus(message[:80])">Before you do that, though, please first try
+            <strong><a href="${trac.homepage}search?ticket=yes&amp;noquickjump=1&amp;q=$q">searching</a>
+            for similar issues</strong>, as it is quite likely that this problem
+            has been reported before. For questions about installation
+            and configuration of Trac, please try the
+            <a href="${trac.homepage}wiki/MailingList">mailing list</a>
+            instead of filing a ticket.
+          </p>
+        </html>""")
+        translator = Translator()
+        translator.setup(tmpl)
+        messages = list(translator.extract(tmpl.stream))
+        self.assertEqual(1, len(messages))
+        self.assertEqual(
+            u'Before you do that, though, please first try\n            '
+            u'[1:[2:searching]\n            for similar issues], as it is '
+            u'quite likely that this problem\n            has been reported '
+            u'before. For questions about installation\n            and '
+            u'configuration of Trac, please try the\n            '
+            u'[3:mailing list]\n            instead of filing a ticket.',
+            messages[0][2]
+        )
+
+    def test_translate_i18n_msg_with_other_directives_nested(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <p i18n:msg="">Before you do that, though, please first try
+            <strong><a href="${trac.homepage}search?ticket=yes&amp;noquickjump=1&amp;q=q">searching</a>
+            for similar issues</strong>, as it is quite likely that this problem
+            has been reported before. For questions about installation
+            and configuration of Trac, please try the
+            <a href="${trac.homepage}wiki/MailingList">mailing list</a>
+            instead of filing a ticket.
+          </p>
+        </html>""")
+        translations = DummyTranslations({
+            u'Before you do that, though, please first try\n            '
+            u'[1:[2:searching]\n            for similar issues], as it is '
+            u'quite likely that this problem\n            has been reported '
+            u'before. For questions about installation\n            and '
+            u'configuration of Trac, please try the\n            '
+            u'[3:mailing list]\n            instead of filing a ticket.':
+                u'Antes de o fazer, porém,\n            '
+                u'[1:por favor tente [2:procurar]\n            por problemas semelhantes], uma vez que '
+                u'é muito provável que este problema\n            já tenha sido reportado '
+                u'anteriormente. Para questões relativas à instalação\n            e '
+                u'configuração do Trac, por favor tente a\n            '
+                u'[3:mailing list]\n            em vez de criar um assunto.'
+        })
+        translator = Translator(translations)
+        translator.setup(tmpl)
+        messages = list(translator.extract(tmpl.stream))
+        self.assertEqual(1, len(messages))
+        ctx = Context()
+        ctx.push({'trac': {'homepage': 'http://trac.edgewall.org/'}})
+        self.assertEqual("""<html>
+          <p>Antes de o fazer, porém,
+            <strong>por favor tente <a href="http://trac.edgewall.org/search?ticket=yes&amp;noquickjump=1&amp;q=q">procurar</a>
+            por problemas semelhantes</strong>, uma vez que é muito provável que este problema
+            já tenha sido reportado anteriormente. Para questões relativas à instalação
+            e configuração do Trac, por favor tente a
+            <a href="http://trac.edgewall.org/wiki/MailingList">mailing list</a>
+            em vez de criar um assunto.</p>
+        </html>""", tmpl.generate(ctx).render())
+
+    def test_i18n_msg_with_other_nested_directives_with_reordered_content(self):
+        # See: http://genshi.edgewall.org/ticket/300#comment:10
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <p py:if="not editable" class="hint" i18n:msg="">
+            <strong>Note:</strong> This repository is defined in
+            <code><a href="${ 'href.wiki(TracIni)' }">trac.ini</a></code>
+            and cannot be edited on this page.
+          </p>
+        </html>""")
+        translations = DummyTranslations({
+            u'[1:Note:] This repository is defined in\n            '
+            u'[2:[3:trac.ini]]\n            and cannot be edited on this page.':
+                u'[1:Nota:] Este repositório está definido em \n           '
+                u'[2:[3:trac.ini]]\n            e não pode ser editado nesta página.',
+        })
+        translator = Translator(translations)
+        translator.setup(tmpl)
+        messages = list(translator.extract(tmpl.stream))
+        self.assertEqual(1, len(messages))
+        self.assertEqual(
+            u'[1:Note:] This repository is defined in\n            '
+            u'[2:[3:trac.ini]]\n            and cannot be edited on this page.',
+            messages[0][2]
+        )
+        self.assertEqual("""<html>
+          <p class="hint"><strong>Nota:</strong> Este repositório está definido em
+           <code><a href="href.wiki(TracIni)">trac.ini</a></code>
+            e não pode ser editado nesta página.</p>
+        </html>""", tmpl.generate(editable=False).render())
+
+    def test_translate_i18n_domain_with_msg_directives(self):
+        #"""translate with i18n:domain and nested i18n:msg directives """
+
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <div i18n:domain="foo">
+            <p i18n:msg="">FooBar</p>
+            <p i18n:msg="">Bar</p>
+          </div>
+        </html>""")
+        translations = DummyTranslations({'Bar': 'Voh'})
+        translations.add_domain('foo', {'FooBar': 'BarFoo', 'Bar': 'PT_Foo'})
+        translator = Translator(translations)
+        translator.setup(tmpl)
+        self.assertEqual("""<html>
+          <div>
+            <p>BarFoo</p>
+            <p>PT_Foo</p>
+          </div>
+        </html>""", tmpl.generate().render())
+
+    def test_translate_i18n_domain_with_inline_directives(self):
+        #"""translate with inlined i18n:domain and i18n:msg directives"""
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <p i18n:msg="" i18n:domain="foo">FooBar</p>
+        </html>""")
+        translations = DummyTranslations({'Bar': 'Voh'})
+        translations.add_domain('foo', {'FooBar': 'BarFoo'})
+        translator = Translator(translations)
+        translator.setup(tmpl)
+        self.assertEqual("""<html>
+          <p>BarFoo</p>
+        </html>""", tmpl.generate().render())
+
+    def test_translate_i18n_domain_without_msg_directives(self):
+        #"""translate domain call without i18n:msg directives still uses current domain"""
+
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <p i18n:msg="">Bar</p>
+          <div i18n:domain="foo">
+            <p i18n:msg="">FooBar</p>
+            <p i18n:msg="">Bar</p>
+            <p>Bar</p>
+          </div>
+          <p>Bar</p>
+        </html>""")
+        translations = DummyTranslations({'Bar': 'Voh'})
+        translations.add_domain('foo', {'FooBar': 'BarFoo', 'Bar': 'PT_Foo'})
+        translator = Translator(translations)
+        translator.setup(tmpl)
+        self.assertEqual("""<html>
+          <p>Voh</p>
+          <div>
+            <p>BarFoo</p>
+            <p>PT_Foo</p>
+            <p>PT_Foo</p>
+          </div>
+          <p>Voh</p>
+        </html>""", tmpl.generate().render())
+
+    def test_translate_i18n_domain_as_directive_not_attribute(self):
+        #"""translate with domain as directive"""
+
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+        <i18n:domain name="foo">
+          <p i18n:msg="">FooBar</p>
+          <p i18n:msg="">Bar</p>
+          <p>Bar</p>
+        </i18n:domain>
+          <p>Bar</p>
+        </html>""")
+        translations = DummyTranslations({'Bar': 'Voh'})
+        translations.add_domain('foo', {'FooBar': 'BarFoo', 'Bar': 'PT_Foo'})
+        translator = Translator(translations)
+        translator.setup(tmpl)
+        self.assertEqual("""<html>
+          <p>BarFoo</p>
+          <p>PT_Foo</p>
+          <p>PT_Foo</p>
+          <p>Voh</p>
+        </html>""", tmpl.generate().render())
+
+    def test_translate_i18n_domain_nested_directives(self):
+        #"""translate with nested i18n:domain directives"""
+
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <p i18n:msg="">Bar</p>
+          <div i18n:domain="foo">
+            <p i18n:msg="">FooBar</p>
+            <p i18n:domain="bar" i18n:msg="">Bar</p>
+            <p>Bar</p>
+          </div>
+          <p>Bar</p>
+        </html>""")
+        translations = DummyTranslations({'Bar': 'Voh'})
+        translations.add_domain('foo', {'FooBar': 'BarFoo', 'Bar': 'foo_Bar'})
+        translations.add_domain('bar', {'Bar': 'bar_Bar'})
+        translator = Translator(translations)
+        translator.setup(tmpl)
+        self.assertEqual("""<html>
+          <p>Voh</p>
+          <div>
+            <p>BarFoo</p>
+            <p>bar_Bar</p>
+            <p>foo_Bar</p>
+          </div>
+          <p>Voh</p>
+        </html>""", tmpl.generate().render())
+
+    def test_translate_i18n_domain_with_empty_nested_domain_directive(self):
+        #"""translate with empty nested i18n:domain directive does not use dngettext"""
+
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <p i18n:msg="">Bar</p>
+          <div i18n:domain="foo">
+            <p i18n:msg="">FooBar</p>
+            <p i18n:domain="" i18n:msg="">Bar</p>
+            <p>Bar</p>
+          </div>
+          <p>Bar</p>
+        </html>""")
+        translations = DummyTranslations({'Bar': 'Voh'})
+        translations.add_domain('foo', {'FooBar': 'BarFoo', 'Bar': 'foo_Bar'})
+        translations.add_domain('bar', {'Bar': 'bar_Bar'})
+        translator = Translator(translations)
+        translator.setup(tmpl)
+        self.assertEqual("""<html>
+          <p>Voh</p>
+          <div>
+            <p>BarFoo</p>
+            <p>Voh</p>
+            <p>foo_Bar</p>
+          </div>
+          <p>Voh</p>
+        </html>""", tmpl.generate().render())
+
+    def test_translate_i18n_choose_as_attribute(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <div i18n:choose="one">
+            <p i18n:singular="">FooBar</p>
+            <p i18n:plural="">FooBars</p>
+          </div>
+          <div i18n:choose="two">
+            <p i18n:singular="">FooBar</p>
+            <p i18n:plural="">FooBars</p>
+          </div>
+        </html>""")
+        translations = DummyTranslations()
+        translator = Translator(translations)
+        translator.setup(tmpl)
+        self.assertEqual("""<html>
+          <div>
+            <p>FooBar</p>
+          </div>
+          <div>
+            <p>FooBars</p>
+          </div>
+        </html>""", tmpl.generate(one=1, two=2).render())
+
+    def test_translate_i18n_choose_as_directive(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+        <i18n:choose numeral="two">
+          <p i18n:singular="">FooBar</p>
+          <p i18n:plural="">FooBars</p>
+        </i18n:choose>
+        <i18n:choose numeral="one">
+          <p i18n:singular="">FooBar</p>
+          <p i18n:plural="">FooBars</p>
+        </i18n:choose>
+        </html>""")
+        translations = DummyTranslations()
+        translator = Translator(translations)
+        translator.setup(tmpl)
+        self.assertEqual("""<html>
+          <p>FooBars</p>
+          <p>FooBar</p>
+        </html>""", tmpl.generate(one=1, two=2).render())
+
+    def test_translate_i18n_choose_as_attribute_with_params(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <div i18n:choose="two; fname, lname">
+            <p i18n:singular="">Foo $fname $lname</p>
+            <p i18n:plural="">Foos $fname $lname</p>
+          </div>
+        </html>""")
+        translations = DummyTranslations({
+            ('Foo %(fname)s %(lname)s', 0): 'Voh %(fname)s %(lname)s',
+            ('Foo %(fname)s %(lname)s', 1): 'Vohs %(fname)s %(lname)s',
+                 'Foo %(fname)s %(lname)s': 'Voh %(fname)s %(lname)s',
+                'Foos %(fname)s %(lname)s': 'Vohs %(fname)s %(lname)s',
+        })
+        translator = Translator(translations)
+        translator.setup(tmpl)
+        self.assertEqual("""<html>
+          <div>
+            <p>Vohs John Doe</p>
+          </div>
+        </html>""", tmpl.generate(two=2, fname='John', lname='Doe').render())
+
+    def test_translate_i18n_choose_as_attribute_with_params_and_domain_as_param(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n"
+            i18n:domain="foo">
+          <div i18n:choose="two; fname, lname">
+            <p i18n:singular="">Foo $fname $lname</p>
+            <p i18n:plural="">Foos $fname $lname</p>
+          </div>
+        </html>""")
+        translations = DummyTranslations()
+        translations.add_domain('foo', {
+            ('Foo %(fname)s %(lname)s', 0): 'Voh %(fname)s %(lname)s',
+            ('Foo %(fname)s %(lname)s', 1): 'Vohs %(fname)s %(lname)s',
+                 'Foo %(fname)s %(lname)s': 'Voh %(fname)s %(lname)s',
+                'Foos %(fname)s %(lname)s': 'Vohs %(fname)s %(lname)s',
+        })
+        translator = Translator(translations)
+        translator.setup(tmpl)
+        self.assertEqual("""<html>
+          <div>
+            <p>Vohs John Doe</p>
+          </div>
+        </html>""", tmpl.generate(two=2, fname='John', lname='Doe').render())
+
+    def test_translate_i18n_choose_as_directive_with_params(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+        <i18n:choose numeral="two" params="fname, lname">
+          <p i18n:singular="">Foo ${fname} ${lname}</p>
+          <p i18n:plural="">Foos ${fname} ${lname}</p>
+        </i18n:choose>
+        <i18n:choose numeral="one" params="fname, lname">
+          <p i18n:singular="">Foo ${fname} ${lname}</p>
+          <p i18n:plural="">Foos ${fname} ${lname}</p>
+        </i18n:choose>
+        </html>""")
+        translations = DummyTranslations({
+            ('Foo %(fname)s %(lname)s', 0): 'Voh %(fname)s %(lname)s',
+            ('Foo %(fname)s %(lname)s', 1): 'Vohs %(fname)s %(lname)s',
+                 'Foo %(fname)s %(lname)s': 'Voh %(fname)s %(lname)s',
+                'Foos %(fname)s %(lname)s': 'Vohs %(fname)s %(lname)s',
+        })
+        translator = Translator(translations)
+        translator.setup(tmpl)
+        self.assertEqual("""<html>
+          <p>Vohs John Doe</p>
+          <p>Voh John Doe</p>
+        </html>""", tmpl.generate(one=1, two=2,
+                                  fname='John', lname='Doe').render())
+
+    def test_translate_i18n_choose_as_directive_with_params_and_domain_as_directive(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+        <i18n:domain name="foo">
+        <i18n:choose numeral="two" params="fname, lname">
+          <p i18n:singular="">Foo ${fname} ${lname}</p>
+          <p i18n:plural="">Foos ${fname} ${lname}</p>
+        </i18n:choose>
+        </i18n:domain>
+        <i18n:choose numeral="one" params="fname, lname">
+          <p i18n:singular="">Foo ${fname} ${lname}</p>
+          <p i18n:plural="">Foos ${fname} ${lname}</p>
+        </i18n:choose>
+        </html>""")
+        translations = DummyTranslations()
+        translations.add_domain('foo', {
+            ('Foo %(fname)s %(lname)s', 0): 'Voh %(fname)s %(lname)s',
+            ('Foo %(fname)s %(lname)s', 1): 'Vohs %(fname)s %(lname)s',
+                 'Foo %(fname)s %(lname)s': 'Voh %(fname)s %(lname)s',
+                'Foos %(fname)s %(lname)s': 'Vohs %(fname)s %(lname)s',
+        })
+        translator = Translator(translations)
+        translator.setup(tmpl)
+        self.assertEqual("""<html>
+          <p>Vohs John Doe</p>
+          <p>Foo John Doe</p>
+        </html>""", tmpl.generate(one=1, two=2,
+                                  fname='John', lname='Doe').render())
+
+    def test_extract_i18n_choose_as_attribute(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <div i18n:choose="one">
+            <p i18n:singular="">FooBar</p>
+            <p i18n:plural="">FooBars</p>
+          </div>
+          <div i18n:choose="two">
+            <p i18n:singular="">FooBar</p>
+            <p i18n:plural="">FooBars</p>
+          </div>
+        </html>""")
+        translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
+        messages = list(translator.extract(tmpl.stream))
+        self.assertEqual(2, len(messages))
+        self.assertEqual((3, 'ngettext', (u'FooBar', u'FooBars'), []), messages[0])
+        self.assertEqual((7, 'ngettext', (u'FooBar', u'FooBars'), []), messages[1])
+
+    def test_extract_i18n_choose_as_directive(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+        <i18n:choose numeral="two">
+          <p i18n:singular="">FooBar</p>
+          <p i18n:plural="">FooBars</p>
+        </i18n:choose>
+        <i18n:choose numeral="one">
+          <p i18n:singular="">FooBar</p>
+          <p i18n:plural="">FooBars</p>
+        </i18n:choose>
+        </html>""")
+        translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
+        messages = list(translator.extract(tmpl.stream))
+        self.assertEqual(2, len(messages))
+        self.assertEqual((3, 'ngettext', (u'FooBar', u'FooBars'), []), messages[0])
+        self.assertEqual((7, 'ngettext', (u'FooBar', u'FooBars'), []), messages[1])
+
+    def test_extract_i18n_choose_as_attribute_with_params(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <div i18n:choose="two; fname, lname">
+            <p i18n:singular="">Foo $fname $lname</p>
+            <p i18n:plural="">Foos $fname $lname</p>
+          </div>
+        </html>""")
+        translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
+        messages = list(translator.extract(tmpl.stream))
+        self.assertEqual(1, len(messages))
+        self.assertEqual((3, 'ngettext', (u'Foo %(fname)s %(lname)s',
+                                          u'Foos %(fname)s %(lname)s'), []),
+                         messages[0])
+
+    def test_extract_i18n_choose_as_attribute_with_params_and_domain_as_param(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n"
+            i18n:domain="foo">
+          <div i18n:choose="two; fname, lname">
+            <p i18n:singular="">Foo $fname $lname</p>
+            <p i18n:plural="">Foos $fname $lname</p>
+          </div>
+        </html>""")
+        translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
+        messages = list(translator.extract(tmpl.stream))
+        self.assertEqual(1, len(messages))
+        self.assertEqual((4, 'ngettext', (u'Foo %(fname)s %(lname)s',
+                                          u'Foos %(fname)s %(lname)s'), []),
+                         messages[0])
+
+    def test_extract_i18n_choose_as_directive_with_params(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+        <i18n:choose numeral="two" params="fname, lname">
+          <p i18n:singular="">Foo ${fname} ${lname}</p>
+          <p i18n:plural="">Foos ${fname} ${lname}</p>
+        </i18n:choose>
+        <i18n:choose numeral="one" params="fname, lname">
+          <p i18n:singular="">Foo ${fname} ${lname}</p>
+          <p i18n:plural="">Foos ${fname} ${lname}</p>
+        </i18n:choose>
+        </html>""")
+        translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
+        messages = list(translator.extract(tmpl.stream))
+        self.assertEqual(2, len(messages))
+        self.assertEqual((3, 'ngettext', (u'Foo %(fname)s %(lname)s',
+                                          u'Foos %(fname)s %(lname)s'), []),
+                         messages[0])
+        self.assertEqual((7, 'ngettext', (u'Foo %(fname)s %(lname)s',
+                                          u'Foos %(fname)s %(lname)s'), []),
+                         messages[1])
+
+    def test_extract_i18n_choose_as_directive_with_params_and_domain_as_directive(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+        <i18n:domain name="foo">
+        <i18n:choose numeral="two" params="fname, lname">
+          <p i18n:singular="">Foo ${fname} ${lname}</p>
+          <p i18n:plural="">Foos ${fname} ${lname}</p>
+        </i18n:choose>
+        </i18n:domain>
+        <i18n:choose numeral="one" params="fname, lname">
+          <p i18n:singular="">Foo ${fname} ${lname}</p>
+          <p i18n:plural="">Foos ${fname} ${lname}</p>
+        </i18n:choose>
+        </html>""")
+        translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
+        messages = list(translator.extract(tmpl.stream))
+        self.assertEqual(2, len(messages))
+        self.assertEqual((4, 'ngettext', (u'Foo %(fname)s %(lname)s',
+                                          u'Foos %(fname)s %(lname)s'), []),
+                         messages[0])
+        self.assertEqual((9, 'ngettext', (u'Foo %(fname)s %(lname)s',
+                                          u'Foos %(fname)s %(lname)s'), []),
+                         messages[1])
+
+    def test_extract_i18n_choose_as_attribute_with_params_and_comment(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <div i18n:choose="two; fname, lname" i18n:comment="As in Foo Bar">
+            <p i18n:singular="">Foo $fname $lname</p>
+            <p i18n:plural="">Foos $fname $lname</p>
+          </div>
+        </html>""")
+        translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
+        messages = list(translator.extract(tmpl.stream))
+        self.assertEqual(1, len(messages))
+        self.assertEqual((3, 'ngettext', (u'Foo %(fname)s %(lname)s',
+                                          u'Foos %(fname)s %(lname)s'),
+                          [u'As in Foo Bar']),
+                         messages[0])
+
+    def test_extract_i18n_choose_as_directive_with_params_and_comment(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+        <i18n:choose numeral="two" params="fname, lname" i18n:comment="As in Foo Bar">
+          <p i18n:singular="">Foo ${fname} ${lname}</p>
+          <p i18n:plural="">Foos ${fname} ${lname}</p>
+        </i18n:choose>
+        </html>""")
+        translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
+        messages = list(translator.extract(tmpl.stream))
+        self.assertEqual(1, len(messages))
+        self.assertEqual((3, 'ngettext', (u'Foo %(fname)s %(lname)s',
+                                          u'Foos %(fname)s %(lname)s'),
+                          [u'As in Foo Bar']),
+                         messages[0])
+
+    def test_translate_i18n_domain_with_nested_inlcudes(self):
+        import os, shutil, tempfile
+        from genshi.template.loader import TemplateLoader
+        dirname = tempfile.mkdtemp(suffix='genshi_test')
+        try:
+            for idx in range(7):
+                file1 = open(os.path.join(dirname, 'tmpl%d.html' % idx), 'w')
+                try:
+                    file1.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude"
+                                         xmlns:py="http://genshi.edgewall.org/"
+                                         xmlns:i18n="http://genshi.edgewall.org/i18n" py:strip="">
+                        <div>Included tmpl$idx</div>
+                        <p i18n:msg="idx">Bar $idx</p>
+                        <p i18n:domain="bar">Bar</p>
+                        <p i18n:msg="idx" i18n:domain="">Bar $idx</p>
+                        <p i18n:domain="" i18n:msg="idx">Bar $idx</p>
+                        <py:if test="idx &lt; 6">
+                        <xi:include href="tmpl${idx}.html" py:with="idx = idx+1"/>
+                        </py:if>
+                    </html>""")
+                finally:
+                    file1.close()
+
+            file2 = open(os.path.join(dirname, 'tmpl10.html'), 'w')
+            try:
+                file2.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude"
+                                     xmlns:py="http://genshi.edgewall.org/"
+                                     xmlns:i18n="http://genshi.edgewall.org/i18n"
+                                     i18n:domain="foo">
+                  <xi:include href="tmpl${idx}.html" py:with="idx = idx+1"/>
+                </html>""")
+            finally:
+                file2.close()
+
+            def callback(template):
+                translations = DummyTranslations({'Bar %(idx)s': 'Voh %(idx)s'})
+                translations.add_domain('foo', {'Bar %(idx)s': 'foo_Bar %(idx)s'})
+                translations.add_domain('bar', {'Bar': 'bar_Bar'})
+                translator = Translator(translations)
+                translator.setup(template)
+            loader = TemplateLoader([dirname], callback=callback)
+            tmpl = loader.load('tmpl10.html')
+
+            self.assertEqual("""<html>
+                        <div>Included tmpl0</div>
+                        <p>foo_Bar 0</p>
+                        <p>bar_Bar</p>
+                        <p>Voh 0</p>
+                        <p>Voh 0</p>
+                        <div>Included tmpl1</div>
+                        <p>foo_Bar 1</p>
+                        <p>bar_Bar</p>
+                        <p>Voh 1</p>
+                        <p>Voh 1</p>
+                        <div>Included tmpl2</div>
+                        <p>foo_Bar 2</p>
+                        <p>bar_Bar</p>
+                        <p>Voh 2</p>
+                        <p>Voh 2</p>
+                        <div>Included tmpl3</div>
+                        <p>foo_Bar 3</p>
+                        <p>bar_Bar</p>
+                        <p>Voh 3</p>
+                        <p>Voh 3</p>
+                        <div>Included tmpl4</div>
+                        <p>foo_Bar 4</p>
+                        <p>bar_Bar</p>
+                        <p>Voh 4</p>
+                        <p>Voh 4</p>
+                        <div>Included tmpl5</div>
+                        <p>foo_Bar 5</p>
+                        <p>bar_Bar</p>
+                        <p>Voh 5</p>
+                        <p>Voh 5</p>
+                        <div>Included tmpl6</div>
+                        <p>foo_Bar 6</p>
+                        <p>bar_Bar</p>
+                        <p>Voh 6</p>
+                        <p>Voh 6</p>
+                </html>""", tmpl.generate(idx=-1).render())
+        finally:
+            shutil.rmtree(dirname)
+
+    def test_translate_i18n_domain_with_nested_inlcudes_with_translatable_attrs(self):
+        import os, shutil, tempfile
+        from genshi.template.loader import TemplateLoader
+        dirname = tempfile.mkdtemp(suffix='genshi_test')
+        try:
+            for idx in range(4):
+                file1 = open(os.path.join(dirname, 'tmpl%d.html' % idx), 'w')
+                try:
+                    file1.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude"
+                                         xmlns:py="http://genshi.edgewall.org/"
+                                         xmlns:i18n="http://genshi.edgewall.org/i18n" py:strip="">
+                        <div>Included tmpl$idx</div>
+                        <p title="${dg('foo', 'Bar %(idx)s') % dict(idx=idx)}" i18n:msg="idx">Bar $idx</p>
+                        <p title="Bar" i18n:domain="bar">Bar</p>
+                        <p title="Bar" i18n:msg="idx" i18n:domain="">Bar $idx</p>
+                        <p i18n:msg="idx" i18n:domain="" title="Bar">Bar $idx</p>
+                        <p i18n:domain="" i18n:msg="idx" title="Bar">Bar $idx</p>
+                        <py:if test="idx &lt; 3">
+                        <xi:include href="tmpl${idx}.html" py:with="idx = idx+1"/>
+                        </py:if>
+                    </html>""")
+                finally:
+                    file1.close()
+
+            file2 = open(os.path.join(dirname, 'tmpl10.html'), 'w')
+            try:
+                file2.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude"
+                                     xmlns:py="http://genshi.edgewall.org/"
+                                     xmlns:i18n="http://genshi.edgewall.org/i18n"
+                                     i18n:domain="foo">
+                  <xi:include href="tmpl${idx}.html" py:with="idx = idx+1"/>
+                </html>""")
+            finally:
+                file2.close()
+
+            translations = DummyTranslations({'Bar %(idx)s': 'Voh %(idx)s',
+                                              'Bar': 'Voh'})
+            translations.add_domain('foo', {'Bar %(idx)s': 'foo_Bar %(idx)s'})
+            translations.add_domain('bar', {'Bar': 'bar_Bar'})
+            translator = Translator(translations)
+
+            def callback(template):
+                translator.setup(template)
+            loader = TemplateLoader([dirname], callback=callback)
+            tmpl = loader.load('tmpl10.html')
+
+            self.assertEqual("""<html>
+                        <div>Included tmpl0</div>
+                        <p title="foo_Bar 0">foo_Bar 0</p>
+                        <p title="bar_Bar">bar_Bar</p>
+                        <p title="Voh">Voh 0</p>
+                        <p title="Voh">Voh 0</p>
+                        <p title="Voh">Voh 0</p>
+                        <div>Included tmpl1</div>
+                        <p title="foo_Bar 1">foo_Bar 1</p>
+                        <p title="bar_Bar">bar_Bar</p>
+                        <p title="Voh">Voh 1</p>
+                        <p title="Voh">Voh 1</p>
+                        <p title="Voh">Voh 1</p>
+                        <div>Included tmpl2</div>
+                        <p title="foo_Bar 2">foo_Bar 2</p>
+                        <p title="bar_Bar">bar_Bar</p>
+                        <p title="Voh">Voh 2</p>
+                        <p title="Voh">Voh 2</p>
+                        <p title="Voh">Voh 2</p>
+                        <div>Included tmpl3</div>
+                        <p title="foo_Bar 3">foo_Bar 3</p>
+                        <p title="bar_Bar">bar_Bar</p>
+                        <p title="Voh">Voh 3</p>
+                        <p title="Voh">Voh 3</p>
+                        <p title="Voh">Voh 3</p>
+                </html>""", tmpl.generate(idx=-1,
+                                          dg=translations.dugettext).render())
+        finally:
+            shutil.rmtree(dirname)
+
+    def test_translate_i18n_msg_and_comment_with_py_strip_directives(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <p i18n:msg="" i18n:comment="As in foo bar" py:strip="">Foo</p>
+          <p py:strip="" i18n:msg="" i18n:comment="As in foo bar">Foo</p>
+        </html>""")
+        translator = Translator(DummyTranslations({'Foo': 'Voh'}))
+        translator.setup(tmpl)
+        self.assertEqual("""<html>
+          Voh
+          Voh
+        </html>""", tmpl.generate().render())
+
+    def test_translate_i18n_choose_and_py_strip(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <div i18n:choose="two; fname, lname">
+            <p i18n:singular="">Foo $fname $lname</p>
+            <p i18n:plural="">Foos $fname $lname</p>
+          </div>
+        </html>""")
+        translations = DummyTranslations({
+            ('Foo %(fname)s %(lname)s', 0): 'Voh %(fname)s %(lname)s',
+            ('Foo %(fname)s %(lname)s', 1): 'Vohs %(fname)s %(lname)s',
+                 'Foo %(fname)s %(lname)s': 'Voh %(fname)s %(lname)s',
+                'Foos %(fname)s %(lname)s': 'Vohs %(fname)s %(lname)s',
+        })
+        translator = Translator(translations)
+        translator.setup(tmpl)
+        self.assertEqual("""<html>
+          <div>
+            <p>Vohs John Doe</p>
+          </div>
+        </html>""", tmpl.generate(two=2, fname='John', lname='Doe').render())
+
+    def test_translate_i18n_choose_and_domain_and_py_strip(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n"
+            i18n:domain="foo">
+          <div i18n:choose="two; fname, lname">
+            <p i18n:singular="">Foo $fname $lname</p>
+            <p i18n:plural="">Foos $fname $lname</p>
+          </div>
+        </html>""")
+        translations = DummyTranslations()
+        translations.add_domain('foo', {
+            ('Foo %(fname)s %(lname)s', 0): 'Voh %(fname)s %(lname)s',
+            ('Foo %(fname)s %(lname)s', 1): 'Vohs %(fname)s %(lname)s',
+                 'Foo %(fname)s %(lname)s': 'Voh %(fname)s %(lname)s',
+                'Foos %(fname)s %(lname)s': 'Vohs %(fname)s %(lname)s',
+        })
+        translator = Translator(translations)
+        translator.setup(tmpl)
+        self.assertEqual("""<html>
+          <div>
+            <p>Vohs John Doe</p>
+          </div>
+        </html>""", tmpl.generate(two=2, fname='John', lname='Doe').render())
+
+    def test_extract_i18n_msg_with_py_strip(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <p i18n:msg="" py:strip="">
+            Please see <a href="help.html">Help</a> for details.
+          </p>
+        </html>""")
+        translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
+        messages = list(translator.extract(tmpl.stream))
+        self.assertEqual(1, len(messages))
+        self.assertEqual((3, None, u'Please see [1:Help] for details.', []),
+                         messages[0])
+
+    def test_extract_i18n_msg_with_py_strip_and_comment(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <p i18n:msg="" py:strip="" i18n:comment="Foo">
+            Please see <a href="help.html">Help</a> for details.
+          </p>
+        </html>""")
+        translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
+        messages = list(translator.extract(tmpl.stream))
+        self.assertEqual(1, len(messages))
+        self.assertEqual((3, None, u'Please see [1:Help] for details.',
+                          ['Foo']), messages[0])
+
+    def test_extract_i18n_choose_as_attribute_and_py_strip(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <div i18n:choose="one" py:strip="">
+            <p i18n:singular="" py:strip="">FooBar</p>
+            <p i18n:plural="" py:strip="">FooBars</p>
+          </div>
+        </html>""")
+        translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
+        messages = list(translator.extract(tmpl.stream))
+        self.assertEqual(1, len(messages))
+        self.assertEqual((3, 'ngettext', (u'FooBar', u'FooBars'), []), messages[0])
+
+    def test_translate_i18n_domain_with_inline_directive_on_START_NS(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n" i18n:domain="foo">
+          <p i18n:msg="">FooBar</p>
+        </html>""")
+        translations = DummyTranslations({'Bar': 'Voh'})
+        translations.add_domain('foo', {'FooBar': 'BarFoo'})
+        translator = Translator(translations)
+        translator.setup(tmpl)
+        self.assertEqual("""<html>
+          <p>BarFoo</p>
+        </html>""", tmpl.generate().render())
+
+    def test_translate_i18n_domain_with_inline_directive_on_START_NS_with_py_strip(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n"
+            i18n:domain="foo" py:strip="">
+          <p i18n:msg="">FooBar</p>
+        </html>""")
+        translations = DummyTranslations({'Bar': 'Voh'})
+        translations.add_domain('foo', {'FooBar': 'BarFoo'})
+        translator = Translator(translations)
+        translator.setup(tmpl)
+        self.assertEqual("""
+          <p>BarFoo</p>
+        """, tmpl.generate().render())
 
 class ExtractTestCase(unittest.TestCase):
 
@@ -504,12 +1463,12 @@
 
     def test_text_template_extraction(self):
         buf = StringIO("""${_("Dear %(name)s") % {'name': name}},
-        
+
         ${ngettext("Your item:", "Your items", len(items))}
         #for item in items
          * $item
         #end
-        
+
         All the best,
         Foobar""")
         results = list(extract(buf, ['_', 'ngettext'], [], {
@@ -563,6 +1522,60 @@
         </html>""")
         self.assertEqual([], list(extract(buf, ['_'], [], {})))
 
+    def test_extract_py_def_directive_with_py_strip(self):
+        # Failed extraction from Trac
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" py:strip="">
+    <py:def function="diff_options_fields(diff)">
+    <label for="style">View differences</label>
+    <select id="style" name="style">
+      <option selected="${diff.style == 'inline' or None}"
+              value="inline">inline</option>
+      <option selected="${diff.style == 'sidebyside' or None}"
+              value="sidebyside">side by side</option>
+    </select>
+    <div class="field">
+      Show <input type="text" name="contextlines" id="contextlines" size="2"
+                  maxlength="3" value="${diff.options.contextlines &lt; 0 and 'all' or diff.options.contextlines}" />
+      <label for="contextlines">lines around each change</label>
+    </div>
+    <fieldset id="ignore" py:with="options = diff.options">
+      <legend>Ignore:</legend>
+      <div class="field">
+        <input type="checkbox" id="ignoreblanklines" name="ignoreblanklines"
+               checked="${options.ignoreblanklines or None}" />
+        <label for="ignoreblanklines">Blank lines</label>
+      </div>
+      <div class="field">
+        <input type="checkbox" id="ignorecase" name="ignorecase"
+               checked="${options.ignorecase or None}" />
+        <label for="ignorecase">Case changes</label>
+      </div>
+      <div class="field">
+        <input type="checkbox" id="ignorewhitespace" name="ignorewhitespace"
+               checked="${options.ignorewhitespace or None}" />
+        <label for="ignorewhitespace">White space changes</label>
+      </div>
+    </fieldset>
+    <div class="buttons">
+      <input type="submit" name="update" value="${_('Update')}" />
+    </div>
+  </py:def></html>""")
+        translator = Translator()
+        tmpl.add_directives(Translator.NAMESPACE, translator)
+        messages = list(translator.extract(tmpl.stream))
+        self.assertEqual(10, len(messages))
+        self.assertEqual([
+            (3, None, u'View differences', []),
+            (6, None, u'inline', []),
+            (8, None, u'side by side', []),
+            (10, None, u'Show', []),
+            (13, None, u'lines around each change', []),
+            (16, None, u'Ignore:', []),
+            (20, None, u'Blank lines', []),
+            (25, None, u'Case changes',[]),
+            (30, None, u'White space changes', []),
+            (34, '_', u'Update', [])], messages)
+
 
 def suite():
     suite = unittest.TestSuite()
Copyright (C) 2012-2017 Edgewall Software