[ZPT] CVS: Zope3/lib/python/Zope/TAL - TALGenerator.py:1.52.16.3.4.8

Barry Warsaw barry@wooz.org
Mon, 10 Jun 2002 15:20:40 -0400


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

Modified Files:
      Tag: fdrake-tal-i18n-branch
	TALGenerator.py 
Log Message:
Support for i18n:name, specifically:

emitI18nVariable(): This gets called whenever we see an i18n:name in a
tag without a tal:replace.  We actually have to handle those two
situations slightly differently, and this is keyed off of whether the
2nd element of the tuple is None or not.

emitStartElement():
- Get the variable name out of the i18ndict, key: "name".
- reword the "mutually exclusive" exceptions to make more sense.
- if we're i18n:replace'ing and we have an i18n:name substitution to
  deal with, then set the todo['i18nvar'] key to a 2-tuple containing
  the variable name and the replacement context.  This will be used
  later by emitEndElement().
- Otherwise, if we've seen an i18n:name with no tal:replacement, then
  set todo['i18nvar'] to a 2-tuple, where the first element is still
  the variable name, but the second is None.  This will be used as a
  special marker by emitI18nVariable().

emitEndElement(): If we're about to emit the end tag, we first need to
make a copy of the current sub-program as it stands before the end tag
is emitted.  That's because in this situation:

    <span i18n:name="name"><b>Jim</b></span>

you want "<b>Jim</b>" to be the implicit value for the key "name" in
the interpolation dictionary, but you do not want "</span>" showing up
there!


=== Zope3/lib/python/Zope/TAL/TALGenerator.py 1.52.16.3.4.7 => 1.52.16.3.4.8 ===
         self.expressionCompiler = expressionCompiler
         self.CompilerError = expressionCompiler.getCompilerError()
+        # This holds the emitted opcodes representing the input
         self.program = []
+        # The program stack for when we need to do some sub-evaluation for an
+        # intermediate result.  E.g. in an i18n:name tag for which the
+        # contents describe the ${name} value.
         self.stack = []
+        # Another stack of postponed actions.  Elements on this stack are a
+        # dictionary; key/values contain useful information that
+        # emitEndElement needs to finish its calculations
         self.todoStack = []
         self.macros = {}
         self.slots = {}
@@ -292,6 +299,30 @@
             #assert 0, 'emitSubstitution...' + `cexpr, attrDict, program`
             self.emit("insertStructure", cexpr, attrDict, program)
 
+    def emitI18nVariable(self, varname, arg):
+        # 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
+        # i18n:name value is taken implicitly from the contents of the tag,
+        # e.g. "I live in <span i18n:name="country">the USA</span>".  In this
+        # case, arg[1] is the opcode sub-program describing the contents of
+        # the tag.
+        #
+        # When arg[0] is not None, it contains the tal expression used to
+        # calculate the contents of the variable, e.g.
+        # "I live in <span i18n:name="country"
+        #                  tal:replace="here/countryOfOrigin" />"
+        key = cexpr = ''
+        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()
+        # XXX Would key be anything but `text'?
+        assert key == 'text'
+        self.emit('i18nVariable', varname, cexpr, program)
+
     def emitTranslation(self, msgid):
         program = self.popProgram()
         self.emit('insertTranslation', msgid, program)
@@ -453,6 +484,7 @@
         # code with the msgid='' and calculate the right implicit msgid during
         # interpretation phase.
         msgid = i18ndict.get("translate")
+        varname = i18ndict.get('name')
 
         if i18ndict.has_key("data") and not i18ndict.has_key("name"):
             raise I18NError("i18n:data must be accompanied by i18n:name",
@@ -464,11 +496,13 @@
                              position)
         if replace:
             if content:
-                raise TALError("content and replace are mutually exclusive",
-                               position)
+                raise TALError(
+                    "tal:content and tal:replace are mutually exclusive",
+                    position)
             if msgid is not None:
-                raise I18NError("i18n:name and replace are mutually exclusive",
-                                position)
+                raise I18NError(
+                    "i18n:translate and tal:replace are mutually exclusive",
+                    position)
 
         repeatWhitespace = None
         if repeat:
@@ -534,7 +568,18 @@
         if content:
             todo["content"] = content
         if replace:
-            todo["replace"] = replace
+            # tal:replace w/ i18n:name has slightly different semantics.  What
+            # we're actually replacing then is the contents of the ${name}
+            # placeholder.
+            if varname:
+                todo['i18nvar'] = (varname, replace)
+            else:
+                todo["replace"] = replace
+            self.pushProgram()
+        # i18n:name w/o tal:replace uses the content as the interpolation
+        # dictionary values
+        elif varname:
+            todo['i18nvar'] = (varname, None)
             self.pushProgram()
         if msgid is not None:
             todo['msgid'] = msgid
@@ -583,7 +628,6 @@
             if not isend:
                 self.emitEndTag(name)
             return
-
         self.position = position = todo.get("position", (None, None))
         defineMacro = todo.get("defineMacro")
         useMacro = todo.get("useMacro")
@@ -599,6 +643,7 @@
         scope = todo.get("scope")
         optTag = todo.get("optional tag")
         msgid = todo.get('msgid')
+        varname = todo.get('i18nvar')
 
         if implied > 0:
             if defineMacro or useMacro or defineSlot or fillSlot:
@@ -617,9 +662,20 @@
         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[:],)
             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
+        # of the expression go into the i18n substitution dictionary.
         if replace:
             self.emitSubstitution(replace, repldict)
+        elif varname:
+            self.emitI18nVariable(varname[0], varname[1:])
         if repeat:
             self.emitRepeat(repeat)
         if condition: