[Zope-Checkins] CVS: Zope/lib/python/TAL - talgettext.py:1.2
Godefroid Chapelle
gotcha at swing.be
Wed Aug 20 18:04:02 EDT 2003
Update of /cvs-repository/Zope/lib/python/TAL
In directory cvs.zope.org:/tmp/cvs-serv28087
Added Files:
talgettext.py
Log Message:
- backported from Z3
- hoping it will become a standard tool as
available with each Zope
=== Zope/lib/python/TAL/talgettext.py 1.1 => 1.2 ===
--- /dev/null Wed Aug 20 17:04:01 2003
+++ Zope/lib/python/TAL/talgettext.py Wed Aug 20 17:04:01 2003
@@ -0,0 +1,314 @@
+
+
+
+#!/usr/bin/env python
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+"""Program to extract internationalization markup from Page Templates.
+
+Once you have marked up a Page Template file with i18n: namespace tags, use
+this program to extract GNU gettext .po file entries.
+
+Usage: talgettext.py [options] files
+Options:
+ -h / --help
+ Print this message and exit.
+ -o / --output <file>
+ Output the translation .po file to <file>.
+ -u / --update <file>
+ Update the existing translation <file> with any new translation strings
+ found.
+"""
+
+import sys
+import time
+import getopt
+import traceback
+
+from TAL.HTMLTALParser import HTMLTALParser
+from TAL.TALInterpreter import TALInterpreter
+from TAL.DummyEngine import DummyEngine
+from ITALES import ITALESEngine
+from TAL.TALDefs import TALESError
+
+__version__ = '$Revision$'
+
+pot_header = '''\
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR ORGANIZATION
+# FIRST AUTHOR <EMAIL at ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\\n"
+"POT-Creation-Date: %(time)s\\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n"
+"Last-Translator: FULL NAME <EMAIL at ADDRESS>\\n"
+"Language-Team: LANGUAGE <LL at li.org>\\n"
+"MIME-Version: 1.0\\n"
+"Content-Type: text/plain; charset=CHARSET\\n"
+"Content-Transfer-Encoding: ENCODING\\n"
+"Generated-By: talgettext.py %(version)s\\n"
+'''
+
+NLSTR = '"\n"'
+
+try:
+ True
+except NameError:
+ True=1
+ False=0
+
+def usage(code, msg=''):
+ # Python 2.1 required
+ print >> sys.stderr, __doc__
+ if msg:
+ print >> sys.stderr, msg
+ sys.exit(code)
+
+
+class POTALInterpreter(TALInterpreter):
+ def translate(self, msgid, default, i18ndict=None, obj=None):
+ # XXX is this right?
+ if i18ndict is None:
+ i18ndict = {}
+ if obj:
+ i18ndict.update(obj)
+ # XXX Mmmh, it seems that sometimes the msgid is None; is that really
+ # possible?
+ if msgid is None:
+ return None
+ # XXX We need to pass in one of context or target_language
+ return self.engine.translate(msgid, self.i18nContext.domain, i18ndict,
+ position=self.position, default=default)
+
+
+class POEngine(DummyEngine):
+ __implements__ = ITALESEngine
+
+ def __init__(self, macros=None):
+ self.catalog = {}
+ DummyEngine.__init__(self, macros)
+
+ def evaluate(*args):
+ return '' # who cares
+
+ def evaluatePathOrVar(*args):
+ return '' # who cares
+
+ def evaluateSequence(self, expr):
+ return (0,) # dummy
+
+ def evaluateBoolean(self, expr):
+ return True # dummy
+
+ def translate(self, msgid, domain=None, mapping=None, default=None,
+ # XXX position is not part of the ITALESEngine
+ # interface
+ position=None):
+
+ if domain not in self.catalog:
+ self.catalog[domain] = {}
+ domain = self.catalog[domain]
+
+ if msgid not in domain:
+ domain[msgid] = []
+ domain[msgid].append((self.file, position))
+ return 'x'
+
+
+class UpdatePOEngine(POEngine):
+ """A slightly-less braindead POEngine which supports loading an existing
+ .po file first."""
+
+ def __init__ (self, macros=None, filename=None):
+ POEngine.__init__(self, macros)
+
+ self._filename = filename
+ self._loadFile()
+ self.base = self.catalog
+ self.catalog = {}
+
+ def __add(self, id, s, fuzzy):
+ "Add a non-fuzzy translation to the dictionary."
+ if not fuzzy and str:
+ # check for multi-line values and munge them appropriately
+ if '\n' in s:
+ lines = s.rstrip().split('\n')
+ s = NLSTR.join(lines)
+ self.catalog[id] = s
+
+ def _loadFile(self):
+ # shamelessly cribbed from Python's Tools/i18n/msgfmt.py
+ # 25-Mar-2003 Nathan R. Yergler (nathan at zope.org)
+ # 14-Apr-2003 Hacked by Barry Warsaw (barry at zope.com)
+
+ ID = 1
+ STR = 2
+
+ try:
+ lines = open(self._filename).readlines()
+ except IOError, msg:
+ print >> sys.stderr, msg
+ sys.exit(1)
+
+ section = None
+ fuzzy = False
+
+ # Parse the catalog
+ lno = 0
+ for l in lines:
+ lno += True
+ # If we get a comment line after a msgstr, this is a new entry
+ if l[0] == '#' and section == STR:
+ self.__add(msgid, msgstr, fuzzy)
+ section = None
+ fuzzy = False
+ # Record a fuzzy mark
+ if l[:2] == '#,' and l.find('fuzzy'):
+ fuzzy = True
+ # Skip comments
+ if l[0] == '#':
+ continue
+ # Now we are in a msgid section, output previous section
+ if l.startswith('msgid'):
+ if section == STR:
+ self.__add(msgid, msgstr, fuzzy)
+ section = ID
+ l = l[5:]
+ msgid = msgstr = ''
+ # Now we are in a msgstr section
+ elif l.startswith('msgstr'):
+ section = STR
+ l = l[6:]
+ # Skip empty lines
+ if not l.strip():
+ continue
+ # XXX: Does this always follow Python escape semantics?
+ l = eval(l)
+ if section == ID:
+ msgid += l
+ elif section == STR:
+ msgstr += '%s\n' % l
+ else:
+ print >> sys.stderr, 'Syntax error on %s:%d' % (infile, lno), \
+ 'before:'
+ print >> sys.stderr, l
+ sys.exit(1)
+ # Add last entry
+ if section == STR:
+ self.__add(msgid, msgstr, fuzzy)
+
+ def evaluate(self, expression):
+ try:
+ return POEngine.evaluate(self, expression)
+ except TALESError:
+ pass
+
+ def evaluatePathOrVar(self, expr):
+ return 'who cares'
+
+ def translate(self, msgid, domain=None, mapping=None, default=None,
+ position=None):
+ if msgid not in self.base:
+ POEngine.translate(self, msgid, domain, mapping, default, position)
+ return 'x'
+
+
+def main():
+ try:
+ opts, args = getopt.getopt(
+ sys.argv[1:],
+ 'ho:u:',
+ ['help', 'output=', 'update='])
+ except getopt.error, msg:
+ usage(1, msg)
+
+ outfile = None
+ engine = None
+ update_mode = False
+ for opt, arg in opts:
+ if opt in ('-h', '--help'):
+ usage(0)
+ elif opt in ('-o', '--output'):
+ outfile = arg
+ elif opt in ('-u', '--update'):
+ update_mode = True
+ if outfile is None:
+ outfile = arg
+ engine = UpdatePOEngine(filename=arg)
+
+ if not args:
+ print 'nothing to do'
+ return
+
+ # We don't care about the rendered output of the .pt file
+ class Devnull:
+ def write(self, s):
+ pass
+
+ # check if we've already instantiated an engine;
+ # if not, use the stupidest one available
+ if not engine:
+ engine = POEngine()
+
+ # process each file specified
+ for filename in args:
+ try:
+ engine.file = filename
+ p = HTMLTALParser()
+ p.parseFile(filename)
+ program, macros = p.getCode()
+ POTALInterpreter(program, macros, engine, stream=Devnull(),
+ metal=False)()
+ except: # Hee hee, I love bare excepts!
+ print 'There was an error processing', filename
+ traceback.print_exc()
+
+ # Now output the keys in the engine. Write them to a file if --output or
+ # --update was specified; otherwise use standard out.
+ if (outfile is None):
+ outfile = sys.stdout
+ else:
+ outfile = file(outfile, update_mode and "a" or "w")
+
+ catalog = {}
+ for domain in engine.catalog.keys():
+ catalog.update(engine.catalog[domain])
+
+ messages = catalog.copy()
+ try:
+ messages.update(engine.base)
+ except AttributeError:
+ pass
+ if '' not in messages:
+ print >> outfile, pot_header % {'time': time.ctime(),
+ 'version': __version__}
+
+ msgids = catalog.keys()
+ # XXX: You should not sort by msgid, but by filename and position. (SR)
+ msgids.sort()
+ for msgid in msgids:
+ positions = catalog[msgid]
+ for filename, position in positions:
+ outfile.write('#: %s:%s\n' % (filename, position[0]))
+
+ outfile.write('msgid "%s"\n' % msgid)
+ outfile.write('msgstr ""\n')
+ outfile.write('\n')
+
+
+if __name__ == '__main__':
+ main()
More information about the Zope-Checkins
mailing list