diff genshi/filters/i18n.py @ 784:67d324a62cc0 experimental-match-fastpaths

update to 0.5.x branch, up through r907 don't know how this fits in with SoC work, but I wanted to do due diligence and keep this branch working in case it someday gets considered for trunk
author aflett
date Mon, 21 Jul 2008 23:17:52 +0000
parents 077c9142dca0
children
line wrap: on
line diff
--- a/genshi/filters/i18n.py
+++ b/genshi/filters/i18n.py
@@ -180,7 +180,10 @@
                     msgbuf.append(kind, data, pos)
                     continue
                 elif i18n_msg in attrs:
-                    msgbuf = MessageBuffer()
+                    params = attrs.get(i18n_msg)
+                    if params and type(params) is list: # event tuple
+                        params = params[0][1]
+                    msgbuf = MessageBuffer(params)
                     attrs -= i18n_msg
 
                 yield kind, (tag, attrs), pos
@@ -189,11 +192,14 @@
                 if not msgbuf:
                     text = data.strip()
                     if text:
-                        data = data.replace(text, translate(text))
+                        data = data.replace(text, unicode(translate(text)))
                     yield kind, data, pos
                 else:
                     msgbuf.append(kind, data, pos)
 
+            elif msgbuf and kind is EXPR:
+                msgbuf.append(kind, data, pos)
+
             elif not skip and msgbuf and kind is END:
                 msgbuf.append(kind, data, pos)
                 if not msgbuf.depth:
@@ -301,7 +307,10 @@
                 if msgbuf:
                     msgbuf.append(kind, data, pos)
                 elif i18n_msg in attrs:
-                    msgbuf = MessageBuffer(pos[1])
+                    params = attrs.get(i18n_msg)
+                    if params and type(params) is list: # event tuple
+                        params = params[0][1]
+                    msgbuf = MessageBuffer(params, pos[1])
 
             elif not skip and search_text and kind is TEXT:
                 if not msgbuf:
@@ -318,6 +327,8 @@
                     msgbuf = None
 
             elif kind is EXPR or kind is EXEC:
+                if msgbuf:
+                    msgbuf.append(kind, data, pos)
                 for funcname, strings in extract_from_code(data,
                                                            gettext_functions):
                     yield pos[1], funcname, strings
@@ -332,26 +343,46 @@
 
 
 class MessageBuffer(object):
-    """Helper class for managing localizable mixed content.
+    """Helper class for managing internationalized mixed content.
     
     :since: version 0.5
     """
 
-    def __init__(self, lineno=-1):
+    def __init__(self, params=u'', lineno=-1):
+        """Initialize the message buffer.
+        
+        :param params: comma-separated list of parameter names
+        :type params: `basestring`
+        :param lineno: the line number on which the first stream event
+                       belonging to the message was found
+        """
+        self.params = [name.strip() for name in params.split(',')]
         self.lineno = lineno
-        self.strings = []
+        self.string = []
         self.events = {}
+        self.values = {}
         self.depth = 1
         self.order = 1
         self.stack = [0]
 
     def append(self, kind, data, pos):
+        """Append a stream event to the buffer.
+        
+        :param kind: the stream event kind
+        :param data: the event data
+        :param pos: the position of the event in the source
+        """
         if kind is TEXT:
-            self.strings.append(data)
+            self.string.append(data)
             self.events.setdefault(self.stack[-1], []).append(None)
+        elif kind is EXPR:
+            param = self.params.pop(0)
+            self.string.append('%%(%s)s' % param)
+            self.events.setdefault(self.stack[-1], []).append(None)
+            self.values[param] = (kind, data, pos)
         else:
             if kind is START:
-                self.strings.append(u'[%d:' % self.order)
+                self.string.append(u'[%d:' % self.order)
                 self.events.setdefault(self.order, []).append((kind, data, pos))
                 self.stack.append(self.order)
                 self.depth += 1
@@ -360,26 +391,83 @@
                 self.depth -= 1
                 if self.depth:
                     self.events[self.stack[-1]].append((kind, data, pos))
-                    self.strings.append(u']')
+                    self.string.append(u']')
                     self.stack.pop()
 
     def format(self):
-        return u''.join(self.strings).strip()
+        """Return a message identifier representing the content in the
+        buffer.
+        """
+        return u''.join(self.string).strip()
 
-    def translate(self, string):
+    def translate(self, string, regex=re.compile(r'%\((\w+)\)s')):
+        """Interpolate the given message translation with the events in the
+        buffer and return the translated stream.
+        
+        :param string: the translated message string
+        """
         parts = parse_msg(string)
         for order, string in parts:
             events = self.events[order]
             while events:
-                event = self.events[order].pop(0)
-                if not event:
+                event = events.pop(0)
+                if event:
+                    yield event
+                else:
                     if not string:
                         break
-                    yield TEXT, string, (None, -1, -1)
+                    for idx, part in enumerate(regex.split(string)):
+                        if idx % 2:
+                            yield self.values[part]
+                        elif part:
+                            yield TEXT, part, (None, -1, -1)
                     if not self.events[order] or not self.events[order][0]:
                         break
-                else:
-                    yield event
+
+
+def parse_msg(string, regex=re.compile(r'(?:\[(\d+)\:)|\]')):
+    """Parse a translated message using Genshi mixed content message
+    formatting.
+
+    >>> parse_msg("See [1:Help].")
+    [(0, 'See '), (1, 'Help'), (0, '.')]
+
+    >>> parse_msg("See [1:our [2:Help] page] for details.")
+    [(0, 'See '), (1, 'our '), (2, 'Help'), (1, ' page'), (0, ' for details.')]
+
+    >>> parse_msg("[2:Details] finden Sie in [1:Hilfe].")
+    [(2, 'Details'), (0, ' finden Sie in '), (1, 'Hilfe'), (0, '.')]
+
+    >>> parse_msg("[1:] Bilder pro Seite anzeigen.")
+    [(1, ''), (0, ' Bilder pro Seite anzeigen.')]
+
+    :param string: the translated message string
+    :return: a list of ``(order, string)`` tuples
+    :rtype: `list`
+    """
+    parts = []
+    stack = [0]
+    while True:
+        mo = regex.search(string)
+        if not mo:
+            break
+
+        if mo.start() or stack[-1]:
+            parts.append((stack[-1], string[:mo.start()]))
+        string = string[mo.end():]
+
+        orderno = mo.group(1)
+        if orderno is not None:
+            stack.append(int(orderno))
+        else:
+            stack.pop()
+        if not stack:
+            break
+
+    if string:
+        parts.append((stack[-1], string))
+
+    return parts
 
 
 def extract_from_code(code, gettext_functions):
@@ -425,46 +513,6 @@
                     yield funcname, strings
     return _walk(code.ast)
 
-def parse_msg(string, regex=re.compile(r'(?:\[(\d+)\:)|\]')):
-    """Parse a message using Genshi compound message formatting.
-
-    >>> parse_msg("See [1:Help].")
-    [(0, 'See '), (1, 'Help'), (0, '.')]
-
-    >>> parse_msg("See [1:our [2:Help] page] for details.")
-    [(0, 'See '), (1, 'our '), (2, 'Help'), (1, ' page'), (0, ' for details.')]
-
-    >>> parse_msg("[2:Details] finden Sie in [1:Hilfe].")
-    [(2, 'Details'), (0, ' finden Sie in '), (1, 'Hilfe'), (0, '.')]
-    
-    >>> parse_msg("[1:] Bilder pro Seite anzeigen.")
-    [(1, ''), (0, ' Bilder pro Seite anzeigen.')]
-    
-    :since: version 0.5
-    """
-    parts = []
-    stack = [0]
-    while True:
-        mo = regex.search(string)
-        if not mo:
-            break
-
-        if mo.start() or stack[-1]:
-            parts.append((stack[-1], string[:mo.start()]))
-        string = string[mo.end():]
-
-        orderno = mo.group(1)
-        if orderno is not None:
-            stack.append(int(orderno))
-        else:
-            stack.pop()
-        if not stack:
-            break
-
-    if string:
-        parts.append((stack[-1], string))
-
-    return parts
 
 def extract(fileobj, keywords, comment_tags, options):
     """Babel extraction method for Genshi templates.
Copyright (C) 2012-2017 Edgewall Software