# HG changeset patch
# Parent 924949fd71673266ccb00169f99ae7cd47496d16
i18n: `MessageBuffer.translate()` now uses the untranslated message as a fallback in case the translated message doesn't parse correctly.

Fixes #426.


diff -r 924949fd7167 -r e2398084da93 genshi/filters/i18n.py
--- a/genshi/filters/i18n.py	Mon May 23 22:45:39 2011 +0200
+++ b/genshi/filters/i18n.py	Mon May 23 22:48:27 2011 +0200
@@ -175,7 +175,8 @@ class MsgDirective(ExtractableI18NDirect
             if previous[0] is not END:
                 msgbuf.append(*previous)
                 previous = None
-            for event in msgbuf.translate(gettext(msgbuf.format())):
+            msg = msgbuf.format()
+            for event in msgbuf.translate(gettext(msg), original=msg):
                 yield event
             if previous:
                 yield previous
@@ -411,10 +412,11 @@ class ChooseDirective(ExtractableI18NDir
             if kind is MSGBUF:
                 for event in choice:
                     if event[0] is MSGBUF:
-                        translation = ngettext(singular_msgbuf.format(),
-                                               plural_msgbuf.format(),
+                        msg = singular_msgbuf.format()
+                        translation = ngettext(msg, plural_msgbuf.format(),
                                                numeral)
-                        for subevent in msgbuf.translate(translation):
+                        for subevent in msgbuf.translate(translation, 
+                                                         original=msg):
                             yield subevent
                     else:
                         yield event
@@ -1015,11 +1017,14 @@ class MessageBuffer(object):
         """
         return ''.join(self.string).strip()
 
-    def translate(self, string, regex=re.compile(r'%\((\w+)\)s')):
+    def translate(self, string, regex=re.compile(r'%\((\w+)\)s'), 
+                  original=None):
         """Interpolate the given message translation with the events in the
         buffer and return the translated stream.
         
         :param string: the translated message string
+        :param original: the untranslated message string, used as a fallback
+                         in case the translated message is unparsable
         """
         substream = None
 
@@ -1034,6 +1039,12 @@ class MessageBuffer(object):
                     )
 
         parts = parse_msg(string)
+        if parts is None:
+            if original is None:
+                raise ValueError("Malformed translated message '%s'" % string)
+            parts = parse_msg(original)
+            if parts is None:
+                raise ValueError("Malformed original message '%s'" % original)
         parts_counter = {}
         for order, string in parts:
             parts_counter.setdefault(order, []).append(None)
@@ -1123,8 +1134,11 @@ def parse_msg(string, regex=re.compile(r
     >>> parse_msg("[1:] Bilder pro Seite anzeigen.")
     [(1, ''), (0, ' Bilder pro Seite anzeigen.')]
     
+    >>> parse_msg("[1:t][2]t")
+
+    
     :param string: the translated message string
-    :return: a list of ``(order, string)`` tuples
+    :return: a list of ``(order, string)`` tuples or `None` if parsing failed.
     :rtype: `list`
     """
     parts = []
@@ -1146,6 +1160,9 @@ def parse_msg(string, regex=re.compile(r
         if not stack:
             break
 
+    if stack != [0]:
+        return None
+
     if string:
         parts.append((stack[-1], string))
 
diff -r 924949fd7167 -r e2398084da93 genshi/filters/tests/i18n.py
--- a/genshi/filters/tests/i18n.py	Mon May 23 22:45:39 2011 +0200
+++ b/genshi/filters/tests/i18n.py	Mon May 23 22:48:27 2011 +0200
@@ -502,6 +502,20 @@ class MsgDirectiveTestCase(unittest.Test
           <p>Für <em>Details</em> siehe bitte <a href="help.html">Hilfe</a>.</p>
         </html>""", tmpl.generate().render())
 
+    def test_translate_i18n_msg_multiple_parse_msg_fallback(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <p i18n:msg="">
+            Please see <a href="help.html">Help</a> for <em>details</em>.
+          </p>
+        </html>""")
+        gettext = lambda s: u"Für [2:Details] siehe bitte [1 Hilfe]."
+        translator = Translator(gettext)
+        translator.setup(tmpl)
+        self.assertEquals("""<html>
+          <p>Please see <a href="help.html">Help</a> for <em>details</em>.</p>
+        </html>""", tmpl.generate().render())
+
     def test_extract_i18n_msg_multiple_empty(self):
         tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
             xmlns:i18n="http://genshi.edgewall.org/i18n">
@@ -1381,6 +1395,34 @@ class ChooseDirectiveTestCase(unittest.T
             <p title="Sachen">Da sind <a href="/things" title="Sachen betrachten">3 Sachen</a>.</p>
         </html>""", tmpl.generate(link="/things", num=3).render(encoding=None))
 
+    def test_translate_i18n_choose_as_element_with_attributes_parse_msg_fallback(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"
+            xmlns:i18n="http://genshi.edgewall.org/i18n">
+          <i18n:choose numeral="num" params="num">
+            <p i18n:singular="" title="Things">
+              There is <a href="$link" title="View thing">${num} thing</a>.
+            </p>
+            <p i18n:plural="" title="Things">
+              There are <a href="$link" title="View things">${num} things</a>.
+            </p>
+          </i18n:choose>
+        </html>""")
+        translations = DummyTranslations({
+            'Things': 'Sachen',
+            'View thing': 'Sache betrachten',
+            'View things': 'Sachen betrachten',
+            ('There is [1:%(num)s thing].', 0): 'Da ist [1 %(num)s Sache].',
+            ('There are [1:%(num)s things].', 1): 'Da sind [1 %(num)s Sachen].'
+        })
+        translator = Translator(translations)
+        translator.setup(tmpl)
+        self.assertEqual(u"""<html>
+            <p title="Sachen">There is <a href="/things" title="Sache betrachten">1 thing</a>.</p>
+        </html>""", tmpl.generate(link="/things", num=1).render(encoding=None))
+        self.assertEqual(u"""<html>
+            <p title="Sachen">There are <a href="/things" title="Sachen betrachten">3 things</a>.</p>
+        </html>""", tmpl.generate(link="/things", num=3).render(encoding=None))
+
     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">
