Annotation of kupuMPIWG/tools/makepox.py, revision 1.1

1.1     ! dwinter     1: """Simple script to generate .pox files
        !             2: 
        !             3:     parses XML for i18n attrs and JS files for _() calls and generates an
        !             4:     XML .pox template document (.poxt file)
        !             5: 
        !             6:     (c) Guido Wesdorp 2005
        !             7: 
        !             8: """
        !             9: 
        !            10: from xml.dom.minidom import parseString, getDOMImplementation
        !            11: import sys, re, os
        !            12: 
        !            13: stderr = sys.stderr
        !            14: 
        !            15: warn_on_broken_xml = True
        !            16: 
        !            17: class POX:
        !            18:     """container for the results"""
        !            19:     def __init__(self):
        !            20:         impl = getDOMImplementation()
        !            21:         self.doc = impl.createDocument(None, 'catalog', None)
        !            22:         self.root = self.doc.documentElement
        !            23:         self.processed = {} # mapping from mid to ([filenames], node)
        !            24: 
        !            25:     def add(self, msgid, filename):
        !            26:         # strip and reduce whitespace
        !            27:         msgid = msgid.strip().replace('\n', ' ').replace('\t', ' ')
        !            28:         while msgid.find('  ') > -1:
        !            29:             msgid.replace('  ', ' ')
        !            30:         if self.processed.has_key(msgid):
        !            31:             filenames, node = self.processed[msgid]
        !            32:             if not filename in filenames:
        !            33:                 filenames.append(filename)
        !            34:                 node.setAttribute('filenames', 
        !            35:                     '%s %s' % (node.getAttribute('filenames'), filename))
        !            36:             return
        !            37:         doc = self.doc
        !            38:         root = self.root
        !            39:         # add the nodes
        !            40:         msgnode = doc.createElement('message')
        !            41:         msgnode.setAttribute('filenames', filename)
        !            42:         root.appendChild(msgnode)
        !            43:         msgidnode = doc.createElement('msgid')
        !            44:         msgidnode.appendChild(doc.createTextNode(msgid))
        !            45:         msgnode.appendChild(msgidnode)
        !            46:         msgstrnode = doc.createElement('msgstr')
        !            47:         msgstrnode.appendChild(doc.createTextNode(msgid))
        !            48:         msgnode.appendChild(msgstrnode)
        !            49:         msgstrnode.setAttribute('i18n:translate', '')
        !            50:         root.appendChild(msgnode)
        !            51:         self.processed[msgid] = ([filename], msgnode)
        !            52: 
        !            53:     def get_result(self):
        !            54:         return self.doc.toprettyxml()
        !            55: 
        !            56: class XMLParser:
        !            57:     """scans XML files (or well-formed HTML files, obviously) for i18 attrs"""
        !            58:     def __init__(self, files, pox):
        !            59:         self._current = None
        !            60:         for file in files:
        !            61:             self.parse_file(file, pox)
        !            62: 
        !            63:     def parse_file(self, filename, pox):
        !            64:         fp = open(filename)
        !            65:         try:
        !            66:             dom = parseString(fp.read())
        !            67:         except:
        !            68:             exc, e, tb = sys.exc_info()
        !            69:             del tb
        !            70:             if warn_on_broken_xml:
        !            71:                 print >>stderr, 'Error parsing %s: %s - %s' % (filename, exc, e)
        !            72:             return
        !            73:         # walk through all the nodes and scan for i18n: stuff
        !            74:         while 1:
        !            75:             node = self.next_node(dom)
        !            76:             if not node:
        !            77:                 break
        !            78:             if node.nodeType == 1:
        !            79:                 attrs = node.attributes
        !            80:                 translate = attrs.getNamedItem('i18n:translate')
        !            81:                 if translate:
        !            82:                     msgid = translate.value
        !            83:                     if not msgid.strip():
        !            84:                         msgid = self.extract_text(node)
        !            85:                     pox.add(msgid, filename)
        !            86:                 attributes = attrs.getNamedItem('i18n:attributes')
        !            87:                 if attributes:
        !            88:                     attributes = [a.strip() for a in 
        !            89:                                         attributes.value.split(';')]
        !            90:                     for attr in attributes:
        !            91:                         attritem = attrs.getNamedItem(attr)
        !            92:                         if not attritem:
        !            93:                             raise AttributeError, \
        !            94:                                 'No %s on %s in %s' % (
        !            95:                                     attr, node.nodeName, filename)
        !            96:                         msgid = attritem.value;
        !            97:                         pox.add(msgid, filename)
        !            98: 
        !            99:     def extract_text(self, node):
        !           100:         xml = ''
        !           101:         for child in node.childNodes:
        !           102:             xml += child.toxml().strip().replace('\n', ' ').replace('\t', ' ')
        !           103:         while xml.find('  ') > -1:
        !           104:             xml = xml.replace('  ', ' ')
        !           105:         return xml
        !           106: 
        !           107:     def next_node(self, dom):
        !           108:         if not self._current or self._current.ownerDocument != dom:
        !           109:             self._current = dom.documentElement
        !           110:         else:
        !           111:             cur = self._current
        !           112:             if cur.hasChildNodes():
        !           113:                 self._current = cur.childNodes[0]
        !           114:             elif cur != cur.parentNode.lastChild:
        !           115:                 self._current = cur.nextSibling
        !           116:             else:
        !           117:                 self._current = cur.parentNode.nextSibling
        !           118:         return self._current
        !           119: 
        !           120: class JSParser:
        !           121:     """scans JS files for _() calls"""
        !           122:     def __init__(self, files, pox):
        !           123:         for file in files:
        !           124:             self.parse_file(file, pox)
        !           125: 
        !           126:     _startfuncreg = re.compile('.*?[^a-zA-Z0-9_]_\(')
        !           127:     _startfuncreg_2 = re.compile('^_\(')
        !           128:     def parse_file(self, filename, pox):
        !           129:         lines = open(filename).readlines()
        !           130:         lineno = 0
        !           131:         more = False
        !           132:         chunks = []
        !           133:         for line in lines:
        !           134:             lineno += 1
        !           135:             if more is True or self._startfuncreg.search(line):
        !           136:                 chunk, more = self._get_func_content(line, filename, 
        !           137:                                                         lineno, more)
        !           138:                 chunks.append(chunk)
        !           139:             if chunks and more is False:
        !           140:                 literal = ''.join(chunks).strip()
        !           141:                 if not literal:
        !           142:                     raise ValueError, ('Unrecognized function content -- ' 
        !           143:                                         'file %s, line %s' % (
        !           144:                                             filename, lineno))
        !           145:                 literal = literal.replace('\t', ' ').replace('\n', ' ')
        !           146:                 while literal.find('  ') > -1:
        !           147:                     literal = literal.replace('  ', ' ')
        !           148:                 more = False
        !           149:                 chunks = []
        !           150:                 pox.add(literal, filename)
        !           151:                 
        !           152:     def _get_func_content(self, line, filename, lineno, more=False):
        !           153:         """return the content of the _() call in line
        !           154: 
        !           155:             if more is True, this will assume the function is already opened
        !           156:             and continue adding to the result from the start of the line 
        !           157:             without searching for '[^a-zA-Z_]_(' first
        !           158: 
        !           159:             returns a tuple (content, more) where more is True if the end of
        !           160:             the function body is not reached, in that case this method should
        !           161:             be called again with the 'more' argument set to True
        !           162:         """
        !           163:         line = line.strip()
        !           164:         if not more:
        !           165:             match = self._startfuncreg.search(line) or \
        !           166:                         self._startfuncreg_2.search(line)
        !           167:             line = line.replace(match.group(0), '')
        !           168:         line = line.strip()
        !           169:         quote = line[0]
        !           170:         line = line[1:]
        !           171:         if not quote in ['"', "'"]:
        !           172:             raise ValueError, ('beginning of function body not a recognized '
        !           173:                                 'quote character: %s -- (file %s, line %s)' % (
        !           174:                                     quote, filename, lineno))
        !           175:         ret = []
        !           176:         previous_char = None
        !           177:         while 1:
        !           178:             new_char = line[0]
        !           179:             line = line[1:]
        !           180:             if new_char == quote:
        !           181:                 if previous_char != '\\':
        !           182:                     break
        !           183:             ret.append(new_char)
        !           184:             previous_char = new_char
        !           185:         
        !           186:         # find out if we should continue after this (do we have a '+' 
        !           187:         # or a ');'?)
        !           188:         more = False
        !           189:         line = line.strip()
        !           190:         if line and line[0] == '+':
        !           191:             line = line[1:].strip()
        !           192:             if line:
        !           193:                 raise ValueError, ('string concatenation only allowed for '
        !           194:                                     'multiline strings, not for variable '
        !           195:                                     'interpolation (use ${} instead) -- '
        !           196:                                     '(file %s, line %s)' % (
        !           197:                                         filename, lineno))
        !           198:             more = True
        !           199:         return ''.join(ret), more
        !           200: 
        !           201: if __name__ == '__main__':
        !           202:     print >>stderr, 'POX extract v0.1'
        !           203:     print >>stderr, '(c) Guido Wesdorp 2004'
        !           204:     files = sys.argv[1:]
        !           205:     print >>stderr, 'Going to parse files', ', '.join(files)
        !           206:     pox = POX()
        !           207:     xml = [f for f in files if not f.endswith('.js')]
        !           208:     js = [f for f in files if f.endswith('.js')]
        !           209:     XMLParser(xml, pox)
        !           210:     JSParser(js, pox)
        !           211:     pres = pox.get_result()
        !           212:     pres = pres.replace('<catalog>',
        !           213:         ('<catalog xmlns:i18n="http://xml.zope.org/namespaces/i18n" '
        !           214:         'i18n:domain="kupu">'))
        !           215:     print pres
        !           216:     print >>stderr, 'Done'

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>