comparison roundup/cgi/TAL/talgettext.py @ 2348:8c2402a78bb0

beginning getting ZPT up to date: TAL first
author Richard Jones <richard@users.sourceforge.net>
date Fri, 21 May 2004 05:36:30 +0000
parents
children 97fae0466d97
comparison
equal deleted inserted replaced
2347:fbbda3b1816d 2348:8c2402a78bb0
1 #!/usr/bin/env python
2 ##############################################################################
3 #
4 # Copyright (c) 2002 Zope Corporation and Contributors.
5 # All Rights Reserved.
6 #
7 # This software is subject to the provisions of the Zope Public License,
8 # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
9 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
10 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
11 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
12 # FOR A PARTICULAR PURPOSE.
13 #
14 ##############################################################################
15 # Modifications for Roundup:
16 # 1. commented out ITALES references
17
18 """Program to extract internationalization markup from Page Templates.
19
20 Once you have marked up a Page Template file with i18n: namespace tags, use
21 this program to extract GNU gettext .po file entries.
22
23 Usage: talgettext.py [options] files
24 Options:
25 -h / --help
26 Print this message and exit.
27 -o / --output <file>
28 Output the translation .po file to <file>.
29 -u / --update <file>
30 Update the existing translation <file> with any new translation strings
31 found.
32 """
33
34 import sys
35 import time
36 import getopt
37 import traceback
38
39 from TAL.HTMLTALParser import HTMLTALParser
40 from TAL.TALInterpreter import TALInterpreter
41 from TAL.DummyEngine import DummyEngine
42 #from ITALES import ITALESEngine
43 from TAL.TALDefs import TALESError
44
45 __version__ = '$Revision: 1.1 $'
46
47 pot_header = '''\
48 # SOME DESCRIPTIVE TITLE.
49 # Copyright (C) YEAR ORGANIZATION
50 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
51 #
52 msgid ""
53 msgstr ""
54 "Project-Id-Version: PACKAGE VERSION\\n"
55 "POT-Creation-Date: %(time)s\\n"
56 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n"
57 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n"
58 "Language-Team: LANGUAGE <LL@li.org>\\n"
59 "MIME-Version: 1.0\\n"
60 "Content-Type: text/plain; charset=CHARSET\\n"
61 "Content-Transfer-Encoding: ENCODING\\n"
62 "Generated-By: talgettext.py %(version)s\\n"
63 '''
64
65 NLSTR = '"\n"'
66
67 try:
68 True
69 except NameError:
70 True=1
71 False=0
72
73 def usage(code, msg=''):
74 # Python 2.1 required
75 print >> sys.stderr, __doc__
76 if msg:
77 print >> sys.stderr, msg
78 sys.exit(code)
79
80
81 class POTALInterpreter(TALInterpreter):
82 def translate(self, msgid, default, i18ndict=None, obj=None):
83 # XXX is this right?
84 if i18ndict is None:
85 i18ndict = {}
86 if obj:
87 i18ndict.update(obj)
88 # XXX Mmmh, it seems that sometimes the msgid is None; is that really
89 # possible?
90 if msgid is None:
91 return None
92 # XXX We need to pass in one of context or target_language
93 return self.engine.translate(msgid, self.i18nContext.domain, i18ndict,
94 position=self.position, default=default)
95
96
97 class POEngine(DummyEngine):
98 #__implements__ = ITALESEngine
99
100 def __init__(self, macros=None):
101 self.catalog = {}
102 DummyEngine.__init__(self, macros)
103
104 def evaluate(*args):
105 return '' # who cares
106
107 def evaluatePathOrVar(*args):
108 return '' # who cares
109
110 def evaluateSequence(self, expr):
111 return (0,) # dummy
112
113 def evaluateBoolean(self, expr):
114 return True # dummy
115
116 def translate(self, msgid, domain=None, mapping=None, default=None,
117 # XXX position is not part of the ITALESEngine
118 # interface
119 position=None):
120
121 if domain not in self.catalog:
122 self.catalog[domain] = {}
123 domain = self.catalog[domain]
124
125 if msgid not in domain:
126 domain[msgid] = []
127 domain[msgid].append((self.file, position))
128 return 'x'
129
130
131 class UpdatePOEngine(POEngine):
132 """A slightly-less braindead POEngine which supports loading an existing
133 .po file first."""
134
135 def __init__ (self, macros=None, filename=None):
136 POEngine.__init__(self, macros)
137
138 self._filename = filename
139 self._loadFile()
140 self.base = self.catalog
141 self.catalog = {}
142
143 def __add(self, id, s, fuzzy):
144 "Add a non-fuzzy translation to the dictionary."
145 if not fuzzy and str:
146 # check for multi-line values and munge them appropriately
147 if '\n' in s:
148 lines = s.rstrip().split('\n')
149 s = NLSTR.join(lines)
150 self.catalog[id] = s
151
152 def _loadFile(self):
153 # shamelessly cribbed from Python's Tools/i18n/msgfmt.py
154 # 25-Mar-2003 Nathan R. Yergler (nathan@zope.org)
155 # 14-Apr-2003 Hacked by Barry Warsaw (barry@zope.com)
156
157 ID = 1
158 STR = 2
159
160 try:
161 lines = open(self._filename).readlines()
162 except IOError, msg:
163 print >> sys.stderr, msg
164 sys.exit(1)
165
166 section = None
167 fuzzy = False
168
169 # Parse the catalog
170 lno = 0
171 for l in lines:
172 lno += True
173 # If we get a comment line after a msgstr, this is a new entry
174 if l[0] == '#' and section == STR:
175 self.__add(msgid, msgstr, fuzzy)
176 section = None
177 fuzzy = False
178 # Record a fuzzy mark
179 if l[:2] == '#,' and l.find('fuzzy'):
180 fuzzy = True
181 # Skip comments
182 if l[0] == '#':
183 continue
184 # Now we are in a msgid section, output previous section
185 if l.startswith('msgid'):
186 if section == STR:
187 self.__add(msgid, msgstr, fuzzy)
188 section = ID
189 l = l[5:]
190 msgid = msgstr = ''
191 # Now we are in a msgstr section
192 elif l.startswith('msgstr'):
193 section = STR
194 l = l[6:]
195 # Skip empty lines
196 if not l.strip():
197 continue
198 # XXX: Does this always follow Python escape semantics?
199 l = eval(l)
200 if section == ID:
201 msgid += l
202 elif section == STR:
203 msgstr += '%s\n' % l
204 else:
205 print >> sys.stderr, 'Syntax error on %s:%d' % (infile, lno), \
206 'before:'
207 print >> sys.stderr, l
208 sys.exit(1)
209 # Add last entry
210 if section == STR:
211 self.__add(msgid, msgstr, fuzzy)
212
213 def evaluate(self, expression):
214 try:
215 return POEngine.evaluate(self, expression)
216 except TALESError:
217 pass
218
219 def evaluatePathOrVar(self, expr):
220 return 'who cares'
221
222 def translate(self, msgid, domain=None, mapping=None, default=None,
223 position=None):
224 if msgid not in self.base:
225 POEngine.translate(self, msgid, domain, mapping, default, position)
226 return 'x'
227
228
229 def main():
230 try:
231 opts, args = getopt.getopt(
232 sys.argv[1:],
233 'ho:u:',
234 ['help', 'output=', 'update='])
235 except getopt.error, msg:
236 usage(1, msg)
237
238 outfile = None
239 engine = None
240 update_mode = False
241 for opt, arg in opts:
242 if opt in ('-h', '--help'):
243 usage(0)
244 elif opt in ('-o', '--output'):
245 outfile = arg
246 elif opt in ('-u', '--update'):
247 update_mode = True
248 if outfile is None:
249 outfile = arg
250 engine = UpdatePOEngine(filename=arg)
251
252 if not args:
253 print 'nothing to do'
254 return
255
256 # We don't care about the rendered output of the .pt file
257 class Devnull:
258 def write(self, s):
259 pass
260
261 # check if we've already instantiated an engine;
262 # if not, use the stupidest one available
263 if not engine:
264 engine = POEngine()
265
266 # process each file specified
267 for filename in args:
268 try:
269 engine.file = filename
270 p = HTMLTALParser()
271 p.parseFile(filename)
272 program, macros = p.getCode()
273 POTALInterpreter(program, macros, engine, stream=Devnull(),
274 metal=False)()
275 except: # Hee hee, I love bare excepts!
276 print 'There was an error processing', filename
277 traceback.print_exc()
278
279 # Now output the keys in the engine. Write them to a file if --output or
280 # --update was specified; otherwise use standard out.
281 if (outfile is None):
282 outfile = sys.stdout
283 else:
284 outfile = file(outfile, update_mode and "a" or "w")
285
286 catalog = {}
287 for domain in engine.catalog.keys():
288 catalog.update(engine.catalog[domain])
289
290 messages = catalog.copy()
291 try:
292 messages.update(engine.base)
293 except AttributeError:
294 pass
295 if '' not in messages:
296 print >> outfile, pot_header % {'time': time.ctime(),
297 'version': __version__}
298
299 msgids = catalog.keys()
300 # XXX: You should not sort by msgid, but by filename and position. (SR)
301 msgids.sort()
302 for msgid in msgids:
303 positions = catalog[msgid]
304 for filename, position in positions:
305 outfile.write('#: %s:%s\n' % (filename, position[0]))
306
307 outfile.write('msgid "%s"\n' % msgid)
308 outfile.write('msgstr ""\n')
309 outfile.write('\n')
310
311
312 if __name__ == '__main__':
313 main()

Roundup Issue Tracker: http://roundup-tracker.org/