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