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 from translate.misc import optrecurse
26
27 optparse = optrecurse.optparse
28 try:
29 from cStringIO import StringIO
30 except ImportError:
31 from StringIO import StringIO
32
34 """a specialized Option Parser for convertor tools..."""
35 - def __init__(self, formats, usetemplates=False, usepots=False, allowmissingtemplate=False, description=None):
42
44 """adds an option to include / exclude fuzzy translations"""
45 fuzzyhelp = "use translations marked fuzzy"
46 nofuzzyhelp = "don't use translations marked fuzzy"
47 if default:
48 fuzzyhelp += " (default)"
49 else:
50 nofuzzyhelp += " (default)"
51 self.add_option("", "--fuzzy", dest="includefuzzy", action="store_true", default=default, help=fuzzyhelp)
52 self.add_option("", "--nofuzzy", dest="includefuzzy", action="store_false", default=default, help=nofuzzyhelp)
53 self.passthrough.append("includefuzzy")
54
56 """adds an option to say what to do with duplicate strings"""
57 self.add_option("", "--duplicates", dest="duplicatestyle", default=default,
58 type="choice", choices=["msgctxt", "merge"],
59 help="what to do with duplicate strings (identical source text): merge, msgctxt (default: '%s')" % default, metavar="DUPLICATESTYLE")
60 self.passthrough.append("duplicatestyle")
61
63 """adds an option to say how to split the po/pot files"""
64 self.add_option("", "--multifile", dest="multifilestyle", default=default,
65 type="choice", choices=["single", "toplevel", "onefile"],
66 help="how to split po/pot files (single, toplevel or onefile)", metavar="MULTIFILESTYLE")
67 self.passthrough.append("multifilestyle")
68
79
90
97
99 """filters output options, processing relevant switches in options"""
100 if self.usepots and options.pot:
101 outputoptions = {}
102 for (inputformat, templateformat), (outputformat, convertor) in self.outputoptions.iteritems():
103 inputformat = self.potifyformat(inputformat)
104 templateformat = self.potifyformat(templateformat)
105 outputformat = self.potifyformat(outputformat)
106 outputoptions[(inputformat, templateformat)] = (outputformat, convertor)
107 return outputoptions
108 else:
109 return self.outputoptions
110
112 """sets the -P/--pot option depending on input/output formats etc"""
113 if self.usepots:
114 potoption = optparse.Option("-P", "--pot", \
115 action="store_true", dest="pot", default=False, \
116 help="output PO Templates (.pot) rather than PO files (.po)")
117 self.define_option(potoption)
118
120 """verifies that the options are valid (required options are present, etc)"""
121 pass
122
123 - def run(self, argv=None):
134
139
140 -def copytemplate(inputfile, outputfile, templatefile, **kwargs):
141 """copies the template file to the output file"""
142 outputfile.write(templatefile.read())
143 return True
144
146 """an object that knows how to replace strings in files"""
147 - def __init__(self, searchstring, replacestring):
148 self.searchstring = searchstring
149 self.replacestring = replacestring
150
152 """actually replace the text"""
153 if self.searchstring is not None and self.replacestring is not None:
154 return text.replace(self.searchstring, self.replacestring)
155 else:
156 return text
157
162
164 """copies the template file to the output file, searching and replacing"""
165 outputfile.write(self.doreplace(templatefile.read()))
166 return True
167
168
169
170
171
172
173
174
175
176
177
178
179
181 """ConvertOptionParser that can handle recursing into single archive files.
182 archiveformats maps extension to class. if the extension doesn't matter, it can be None.
183 if the extension is only valid for input/output/template, it can be given as (extension, filepurpose)"""
184 - def __init__(self, formats, usetemplates=False, usepots=False, description=None, archiveformats=None):
185 if archiveformats is None:
186 self.archiveformats = {}
187 else:
188 self.archiveformats = archiveformats
189 self.archiveoptions = {}
190 ConvertOptionParser.__init__(self, formats, usetemplates, usepots, description=description)
191
193 """allows setting options that will always be passed to openarchive"""
194 self.archiveoptions = kwargs
195
196 - def isrecursive(self, fileoption, filepurpose='input'):
201
202 - def isarchive(self, fileoption, filepurpose='input'):
203 """returns whether the file option is an archive file"""
204 if not isinstance(fileoption, (str, unicode)):
205 return False
206 mustexist = (filepurpose != 'output')
207 if mustexist and not os.path.isfile(fileoption):
208 return False
209 fileext = self.splitext(fileoption)[1]
210
211 return self.getarchiveclass(fileext, filepurpose, os.path.isdir(fileoption)) is not None
212
214 """returns the archiveclass for the given fileext and filepurpose"""
215 archiveclass = self.archiveformats.get(fileext, None)
216 if archiveclass is not None:
217 return archiveclass
218 archiveclass = self.archiveformats.get((fileext, filepurpose), None)
219 if archiveclass is not None:
220 return archiveclass
221 if not isdir:
222 archiveclass = self.archiveformats.get(None, None)
223 if archiveclass is not None:
224 return archiveclass
225 archiveclass = self.archiveformats.get((None, filepurpose), None)
226 if archiveclass is not None:
227 return archiveclass
228 return None
229
230 - def openarchive(self, archivefilename, filepurpose, **kwargs):
231 """creates an archive object for the given file"""
232 archiveext = self.splitext(archivefilename)[1]
233 archiveclass = self.getarchiveclass(archiveext, filepurpose, os.path.isdir(archivefilename))
234 archiveoptions = self.archiveoptions.copy()
235 archiveoptions.update(kwargs)
236 return archiveclass(archivefilename, **archiveoptions)
237
245
247 """recurse through archive files and convert files"""
248 inputfiles = []
249 for inputpath in options.inputarchive:
250 if self.isexcluded(options, inputpath):
251 continue
252 top, name = os.path.split(inputpath)
253 if not self.isvalidinputname(options, name):
254 continue
255 inputfiles.append(inputpath)
256 return inputfiles
257
264
271
282
284 """gets the absolute path to a template file"""
285 if templatepath is not None and self.usetemplates and options.template:
286 if self.isarchive(options.template, 'template'):
287 return templatepath
288 elif not options.recursivetemplate:
289 return templatepath
290 else:
291 return os.path.join(options.template, templatepath)
292 else:
293 return None
294
302
304 """gets the absolute path to an output file"""
305 if self.isarchive(options.output, 'output'):
306 return outputpath
307 elif options.recursiveoutput and options.output:
308 return os.path.join(options.output, outputpath)
309 else:
310 return outputpath
311
316
327
329 """opens the templatearchive if not already open"""
330 if not self.usetemplates:
331 return
332 if options.template and self.isarchive(options.template, 'template') and not hasattr(options, "templatearchive"):
333 options.templatearchive = self.openarchive(options.template, 'template')
334
339
351
352 - def processfile(self, fileprocessor, options, fullinputpath, fulloutputpath, fulltemplatepath):
353 """run an invidividual conversion"""
354 if self.isarchive(options.output, 'output'):
355 inputfile = self.openinputfile(options, fullinputpath)
356
357 templatefile = self.opentemplatefile(options, fulltemplatepath)
358 outputfile = self.openoutputfile(options, fulloutputpath)
359 passthroughoptions = self.getpassthroughoptions(options)
360 if fileprocessor(inputfile, outputfile, templatefile, **passthroughoptions):
361 if not outputfile.isatty():
362 outputfile.close()
363 return True
364 else:
365 if fulloutputpath and os.path.isfile(fulloutputpath):
366 outputfile.close()
367 os.unlink(fulloutputpath)
368 return False
369 else:
370 return super(ArchiveConvertOptionParser, self).processfile(fileprocessor, options, fullinputpath, fulloutputpath, fulltemplatepath)
371
372 -def main(argv=None):
373 parser = ArchiveConvertOptionParser({}, description=__doc__)
374 parser.run(argv)
375