1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """Handles converting of files between formats (used by translate.convert tools)"""
23
24 import os.path
25 try:
26 from cStringIO import StringIO
27 except ImportError:
28 from StringIO import StringIO
29
30 from translate.misc import optrecurse
31
32 optparse = optrecurse.optparse
33
34
36 """a specialized Option Parser for convertor tools..."""
37
38 - def __init__(self, formats, usetemplates=False, usepots=False, allowmissingtemplate=False, description=None):
45
47 """adds an option to include / exclude fuzzy translations"""
48 fuzzyhelp = "use translations marked fuzzy"
49 nofuzzyhelp = "don't use translations marked fuzzy"
50 if default:
51 fuzzyhelp += " (default)"
52 else:
53 nofuzzyhelp += " (default)"
54 self.add_option("", "--fuzzy", dest="includefuzzy", action="store_true", default=default, help=fuzzyhelp)
55 self.add_option("", "--nofuzzy", dest="includefuzzy", action="store_false", default=default, help=nofuzzyhelp)
56 self.passthrough.append("includefuzzy")
57
59 """adds an option to say what to do with duplicate strings"""
60 self.add_option("", "--duplicates", dest="duplicatestyle", default=default,
61 type="choice", choices=["msgctxt", "merge"],
62 help="what to do with duplicate strings (identical source text): merge, msgctxt (default: '%s')" % default, metavar="DUPLICATESTYLE")
63 self.passthrough.append("duplicatestyle")
64
66 """adds an option to say how to split the po/pot files"""
67 self.add_option("", "--multifile", dest="multifilestyle", default=default,
68 type="choice", choices=["single", "toplevel", "onefile"],
69 help="how to split po/pot files (single, toplevel or onefile)", metavar="MULTIFILESTYLE")
70 self.passthrough.append("multifilestyle")
71
82
93
100
102 """filters output options, processing relevant switches in options"""
103 if self.usepots and options.pot:
104 outputoptions = {}
105 for (inputformat, templateformat), (outputformat, convertor) in self.outputoptions.iteritems():
106 inputformat = self.potifyformat(inputformat)
107 templateformat = self.potifyformat(templateformat)
108 outputformat = self.potifyformat(outputformat)
109 outputoptions[(inputformat, templateformat)] = (outputformat, convertor)
110 return outputoptions
111 else:
112 return self.outputoptions
113
115 """sets the -P/--pot option depending on input/output formats etc"""
116 if self.usepots:
117 potoption = optparse.Option("-P", "--pot", \
118 action="store_true", dest="pot", default=False, \
119 help="output PO Templates (.pot) rather than PO files (.po)")
120 self.define_option(potoption)
121
123 """verifies that the options are valid (required options are present, etc)"""
124 pass
125
126 - def run(self, argv=None):
137
138
143
144
145 -def copytemplate(inputfile, outputfile, templatefile, **kwargs):
146 """copies the template file to the output file"""
147 outputfile.write(templatefile.read())
148 return True
149
150
152 """an object that knows how to replace strings in files"""
153
154 - def __init__(self, searchstring, replacestring):
155 self.searchstring = searchstring
156 self.replacestring = replacestring
157
159 """actually replace the text"""
160 if self.searchstring is not None and self.replacestring is not None:
161 return text.replace(self.searchstring, self.replacestring)
162 else:
163 return text
164
169
171 """copies the template file to the output file, searching and replacing"""
172 outputfile.write(self.doreplace(templatefile.read()))
173 return True
174
175
176
177
178
179
180
181
182
183
184
185
186
187
189 """ConvertOptionParser that can handle recursing into single archive files.
190 archiveformats maps extension to class. if the extension doesn't matter, it can be None.
191 if the extension is only valid for input/output/template, it can be given as (extension, filepurpose)"""
192
193 - def __init__(self, formats, usetemplates=False, usepots=False, description=None, archiveformats=None):
194 if archiveformats is None:
195 self.archiveformats = {}
196 else:
197 self.archiveformats = archiveformats
198 self.archiveoptions = {}
199 ConvertOptionParser.__init__(self, formats, usetemplates, usepots, description=description)
200
202 """allows setting options that will always be passed to openarchive"""
203 self.archiveoptions = kwargs
204
205 - def isrecursive(self, fileoption, filepurpose='input'):
210
211 - def isarchive(self, fileoption, filepurpose='input'):
212 """returns whether the file option is an archive file"""
213 if not isinstance(fileoption, (str, unicode)):
214 return False
215 mustexist = (filepurpose != 'output')
216 if mustexist and not os.path.isfile(fileoption):
217 return False
218 fileext = self.splitext(fileoption)[1]
219
220 return self.getarchiveclass(fileext, filepurpose, os.path.isdir(fileoption)) is not None
221
223 """returns the archiveclass for the given fileext and filepurpose"""
224 archiveclass = self.archiveformats.get(fileext, None)
225 if archiveclass is not None:
226 return archiveclass
227 archiveclass = self.archiveformats.get((fileext, filepurpose), None)
228 if archiveclass is not None:
229 return archiveclass
230 if not isdir:
231 archiveclass = self.archiveformats.get(None, None)
232 if archiveclass is not None:
233 return archiveclass
234 archiveclass = self.archiveformats.get((None, filepurpose), None)
235 if archiveclass is not None:
236 return archiveclass
237 return None
238
239 - def openarchive(self, archivefilename, filepurpose, **kwargs):
240 """creates an archive object for the given file"""
241 archiveext = self.splitext(archivefilename)[1]
242 archiveclass = self.getarchiveclass(archiveext, filepurpose, os.path.isdir(archivefilename))
243 archiveoptions = self.archiveoptions.copy()
244 archiveoptions.update(kwargs)
245 return archiveclass(archivefilename, **archiveoptions)
246
254
256 """recurse through archive files and convert files"""
257 inputfiles = []
258 for inputpath in options.inputarchive:
259 if self.isexcluded(options, inputpath):
260 continue
261 top, name = os.path.split(inputpath)
262 if not self.isvalidinputname(options, name):
263 continue
264 inputfiles.append(inputpath)
265 return inputfiles
266
273
280
291
293 """gets the absolute path to a template file"""
294 if templatepath is not None and self.usetemplates and options.template:
295 if self.isarchive(options.template, 'template'):
296 return templatepath
297 elif not options.recursivetemplate:
298 return templatepath
299 else:
300 return os.path.join(options.template, templatepath)
301 else:
302 return None
303
311
313 """gets the absolute path to an output file"""
314 if self.isarchive(options.output, 'output'):
315 return outputpath
316 elif options.recursiveoutput and options.output:
317 return os.path.join(options.output, outputpath)
318 else:
319 return outputpath
320
325
336
338 """opens the templatearchive if not already open"""
339 if not self.usetemplates:
340 return
341 if options.template and self.isarchive(options.template, 'template') and not hasattr(options, "templatearchive"):
342 options.templatearchive = self.openarchive(options.template, 'template')
343
348
360
361 - def processfile(self, fileprocessor, options, fullinputpath, fulloutputpath, fulltemplatepath):
362 """run an invidividual conversion"""
363 if self.isarchive(options.output, 'output'):
364 inputfile = self.openinputfile(options, fullinputpath)
365
366 templatefile = self.opentemplatefile(options, fulltemplatepath)
367 outputfile = self.openoutputfile(options, fulloutputpath)
368 passthroughoptions = self.getpassthroughoptions(options)
369 if fileprocessor(inputfile, outputfile, templatefile, **passthroughoptions):
370 if not outputfile.isatty():
371 outputfile.close()
372 return True
373 else:
374 if fulloutputpath and os.path.isfile(fulloutputpath):
375 outputfile.close()
376 os.unlink(fulloutputpath)
377 return False
378 else:
379 return super(ArchiveConvertOptionParser, self).processfile(fileprocessor, options, fullinputpath, fulloutputpath, fulltemplatepath)
380
381
382 -def main(argv=None):
383 parser = ArchiveConvertOptionParser({}, description=__doc__)
384 parser.run(argv)
385