[Zope-Checkins] CVS: Zope3/lib/python/Zope/TAL - TALGenerator.py:1.59 TALInterpreter.py:1.80 driver.py:1.31

Barry Warsaw barry@wooz.org
Mon, 1 Jul 2002 11:59:12 -0400


Update of /cvs-repository/Zope3/lib/python/Zope/TAL
In directory cvs.zope.org:/tmp/cvs-serv5877/lib/python/Zope/TAL

Modified Files:
	TALGenerator.py TALInterpreter.py driver.py 
Log Message:
Merging tal-i18n-refactor-branch back into the Zope3 trunk.


=== Zope3/lib/python/Zope/TAL/TALGenerator.py 1.58 => 1.59 ===
 from TranslationContext import TranslationContext, DEFAULT_DOMAIN
 
+I18N_REPLACE = 1
+I18N_CONTENT = 2
+I18N_EXPRESSION = 3
+
 class TALGenerator:
 
     inMacroUse = 0
@@ -91,6 +95,12 @@
                 # instructions to be joined together.
                 output.append(self.optimizeArgsList(item))
                 continue
+            if opcode == 'noop':
+                # This is a spacer for end tags in the face of i18n:name
+                # attributes.  We can't let the optimizer collect immediately
+                # following end tags into the same rawtextOffset.
+                opcode = None
+                pass
             text = "".join(collect)
             if text:
                 i = text.rfind("\n")
@@ -299,7 +309,7 @@
             assert key == "structure"
             self.emit("insertStructure", cexpr, attrDict, program)
 
-    def emitI18nVariable(self, varname, arg):
+    def emitI18nVariable(self, varname, action, expression):
         # Used for i18n:name attributes.  arg is extra information describing
         # how the contents of the variable should get filled in, and it will
         # either be a 1-tuple or a 2-tuple.  If arg[0] is None, then the
@@ -313,15 +323,24 @@
         # "I live in <span i18n:name="country"
         #                  tal:replace="here/countryOfOrigin" />"
         key = cexpr = None
-        if arg[0] is not None:
-            key, expr = parseSubstitution(arg[0])
-            cexpr = self.compileExpression(expr)
-        else:
-            cexpr = self.optimize(arg[1][1:])
         program = self.popProgram()
+        if action == I18N_REPLACE:
+            # This is a tag with an i18n:name and a tal:replace (implicit or
+            # explicit).  Get rid of the first and last elements of the
+            # program, which are the start and end tag opcodes of the tag.
+            program = program[1:-1]
+        elif action == I18N_CONTENT:
+            # This is a tag with an i18n:name and a tal:content
+            # (explicit-only).  Keep the first and last elements of the
+            # program, so we keep the start and end tag output.
+            pass
+        else:
+            assert action == I18N_EXPRESSION
+            key, expr = parseSubstitution(expression)
+            cexpr = self.compileExpression(expr)
         # XXX Would key be anything but 'text' or None?
         assert key in ('text', None)
-        self.emit('i18nVariable', varname, cexpr, program)
+        self.emit('i18nVariable', varname, program, cexpr)
 
     def emitTranslation(self, msgid, i18ndata):
         program = self.popProgram()
@@ -685,19 +704,30 @@
             raise exc("%s attributes on <%s> require explicit </%s>" %
                       (what, name, name), position)
 
+        # If there's no tal:content or tal:replace in the tag with the
+        # i18n:name, tal:replace is the default.
+        i18nNameAction = I18N_REPLACE
         if content:
+            if varname:
+                i18nNameAction = I18N_CONTENT
             self.emitSubstitution(content, {})
-        if msgid is not None:
+        # If we're looking at an implicit msgid, emit the insertTranslation
+        # opcode now, so that the end tag doesn't become part of the implicit
+        # msgid.  If we're looking at an explicit msgid, it's better to emit
+        # the opcode after the i18nVariable opcode so we can better handle
+        # tags with both of them in them (and in the latter case, the contents
+        # would be thrown away for msgid purposes).
+        if msgid is not None and not varname:
             self.emitTranslation(msgid, i18ndata)
         if optTag:
             self.emitOptTag(name, optTag, isend)
         elif not isend:
-            # Before we emit the end tag, we need to see if the value of the
-            # current program is to be used as the value of the i18n:name
-            # interpolation variable.  If so, we need to make a copy of the
-            # program /without the end tag/ and squirrel it away for later.
-            if not replace and varname and varname[1] is None:
-                varname += (self.program[:],)
+            # If we're processing the end tag for a tag that contained
+            # i18n:name, we need to make sure that optimize() won't collect
+            # immediately following end tags into the same rawtextOffset, so
+            # put a spacer here that the optimizer will recognize.
+            if varname:
+                self.emit('noop')
             self.emitEndTag(name)
         # If i18n:name appeared in the same tag as tal:replace then we're
         # going to do the substitution a little bit differently.  The results
@@ -705,7 +735,20 @@
         if replace:
             self.emitSubstitution(replace, repldict)
         elif varname:
-            self.emitI18nVariable(varname[0], varname[1:])
+            if varname[1] is not None:
+                i18nNameAction = I18N_EXPRESSION
+            # o varname[0] is the variable name
+            # o i18nNameAction is either
+            #   - I18N_REPLACE for implicit tal:replace
+            #   - I18N_CONTENT for tal:content
+            #   - I18N_EXPRESSION for explicit tal:replace
+            # o varname[1] will be None for the first two actions and the
+            #   replacement tal expression for the third action.
+            self.emitI18nVariable(varname[0], i18nNameAction, varname[1])
+        # Do not test for "msgid is not None", i.e. we only want to test for
+        # explicit msgids here.  See comment above.
+        if msgid is not None and varname:
+            self.emitTranslation(msgid, i18ndata)
         if repeat:
             self.emitRepeat(repeat)
         if condition:


=== Zope3/lib/python/Zope/TAL/TALInterpreter.py 1.79 => 1.80 ===
 ]
 
+def normalize(text):
+    # Now we need to normalize the whitespace in implicit message ids and
+    # implicit $name substitution values by stripping leading and trailing
+    # whitespace, and folding all internal whitespace to a single space.
+    return ' '.join(text.split())
+
+
 class AltTALGenerator(TALGenerator):
 
     def __init__(self, repldict, expressionCompiler=None, xml=0):
@@ -192,8 +199,8 @@
         try:
             if self.debug:
                 for (opcode, args) in program:
-                    s = "%sdo_%s%s\n" % ("    "*self.level, opcode,
-                                      repr(args))
+                    s = "%sdo_%s(%s)\n" % ("    "*self.level, opcode,
+                                           repr(args))
                     if len(s) > 80:
                         s = s[:76] + "...\n"
                     sys.stderr.write(s)
@@ -470,21 +477,21 @@
     bytecode_handlers["insertText"] = do_insertText
 
     def do_i18nVariable(self, stuff):
-        varname = stuff[0]
-        if isinstance(stuff[1], ListType):
+        varname, program, expression = stuff
+        if expression is None:
             # The value is implicitly the contents of this tag, so we have to
-            # evaluate the mini-program
+            # evaluate the mini-program to get the value of the variable.
             state = self.saveState()
             try:
                 tmpstream = StringIO()
-                self.interpret(stuff[1], tmpstream)
-                value = tmpstream.getvalue()
+                self.interpret(program, tmpstream)
+                value = normalize(tmpstream.getvalue())
             finally:
                 self.restoreState(state)
         else:
             # Evaluate the value to be associated with the variable in the
             # i18n interpolation dictionary.
-            value = self.engine.evaluate(stuff[1])
+            value = self.engine.evaluate(expression)
         # Either the i18n:name tag is nested inside an i18n:translate in which
         # case the last item on the stack has the i18n dictionary and string
         # representation, or the i18n:name and i18n:translate attributes are
@@ -517,11 +524,7 @@
         # message id.  All other useful information will be in the i18ndict on
         # the top of the i18nStack.
         if msgid == '':
-            msgid = tmpstream.getvalue()
-            # Now we need to normalize the whitespace in the implicit message
-            # id by stripping leading and trailing whitespace, and folding all
-            # internal whitespace to a single space.
-            msgid = ' '.join(msgid.split())
+            msgid = normalize(tmpstream.getvalue())
         self.i18nStack.pop()
         # See if there is was an i18n:data for msgid
         if len(stuff) > 2:


=== Zope3/lib/python/Zope/TAL/driver.py 1.30 => 1.31 ===
         Leave TAL/METAL attributes in output
     -i
-        Leave I18N substitution strings un-interpolated
+        Leave I18N substitution strings un-interpolated.
 """
 
 import os
@@ -58,6 +58,13 @@
             return '%(minutes)s minutes after %(hours)s %(ampm)s' % mapping
         elif msgid == 'jobnum':
             return '%(jobnum)s is the JOB NUMBER' % mapping
+        elif msgid == 'verify':
+            s = 'Your contact email address is recorded as %(email)s'
+            return s % mapping
+        elif msgid == 'mailto:${request/submitter}':
+            return 'mailto:bperson@dom.ain'
+        elif msgid == 'origin':
+            return '%(name)s was born in %(country)s' % mapping
         return DummyTranslationService.translate(self, domain, msgid,
                                                  mapping, context,
                                                  target_language)
@@ -75,6 +82,8 @@
                     }
         elif expr == 'context/@@object_name':
             return '7'
+        elif expr == 'request/submitter':
+            return 'aperson@dom.ain'
         return DummyEngine.evaluatePathOrVar(self, expr)
 
 
@@ -84,6 +93,12 @@
 ENGINES = {'test23.html': TestEngine,
            'test24.html': TestEngine,
            'test26.html': TestEngine,
+           'test27.html': TestEngine,
+           'test28.html': TestEngine,
+           'test29.html': TestEngine,
+           'test30.html': TestEngine,
+           'test31.html': TestEngine,
+           'test32.html': TestEngine,
            }
 
 def usage(code, msg=''):