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):
131
136
137 -def copytemplate(inputfile, outputfile, templatefile, **kwargs):
138 """copies the template file to the output file"""
139 outputfile.write(templatefile.read())
140 return True
141
143 """an object that knows how to replace strings in files"""
144 - def __init__(self, searchstring, replacestring):
145 self.searchstring = searchstring
146 self.replacestring = replacestring
147
149 """actually replace the text"""
150 if self.searchstring is not None and self.replacestring is not None:
151 return text.replace(self.searchstring, self.replacestring)
152 else:
153 return text
154
159
161 """copies the template file to the output file, searching and replacing"""
162 outputfile.write(self.doreplace(templatefile.read()))
163 return True
164
165
166
167
168
169
170
171
172
173
174
175
176
178 """ConvertOptionParser that can handle recursing into single archive files.
179 archiveformats maps extension to class. if the extension doesn't matter, it can be None.
180 if the extension is only valid for input/output/template, it can be given as (extension, filepurpose)"""
181 - def __init__(self, formats, usetemplates=False, usepots=False, description=None, archiveformats=None):
182 if archiveformats is None:
183 self.archiveformats = {}
184 else:
185 self.archiveformats = archiveformats
186 self.archiveoptions = {}
187 ConvertOptionParser.__init__(self, formats, usetemplates, usepots, description=description)
188
190 """allows setting options that will always be passed to openarchive"""
191 self.archiveoptions = kwargs
192
193 - def isrecursive(self, fileoption, filepurpose='input'):
197
198 - def isarchive(self, fileoption, filepurpose='input'):
199 """returns whether the file option is an archive file"""
200 if not isinstance(fileoption, (str, unicode)):
201 return False
202 mustexist = (filepurpose != 'output')
203 if mustexist and not os.path.isfile(fileoption):
204 return False
205 fileext = self.splitext(fileoption)[1]
206
207 return self.getarchiveclass(fileext, filepurpose, os.path.isdir(fileoption)) is not None
208
210 """returns the archiveclass for the given fileext and filepurpose"""
211 archiveclass = self.archiveformats.get(fileext, None)
212 if archiveclass is not None:
213 return archiveclass
214 archiveclass = self.archiveformats.get((fileext, filepurpose), None)
215 if archiveclass is not None:
216 return archiveclass
217 if not isdir:
218 archiveclass = self.archiveformats.get(None, None)
219 if archiveclass is not None:
220 return archiveclass
221 archiveclass = self.archiveformats.get((None, filepurpose), None)
222 if archiveclass is not None:
223 return archiveclass
224 return None
225
226 - def openarchive(self, archivefilename, filepurpose, **kwargs):
227 """creates an archive object for the given file"""
228 archiveext = self.splitext(archivefilename)[1]
229 archiveclass = self.getarchiveclass(archiveext, filepurpose, os.path.isdir(archivefilename))
230 archiveoptions = self.archiveoptions.copy()
231 archiveoptions.update(kwargs)
232 return archiveclass(archivefilename, **archiveoptions)
233
241
243 """recurse through archive files and convert files"""
244 inputfiles = []
245 for inputpath in options.inputarchive:
246 if self.isexcluded(options, inputpath):
247 continue
248 top, name = os.path.split(inputpath)
249 if not self.isvalidinputname(options, name):
250 continue
251 inputfiles.append(inputpath)
252 return inputfiles
253
260
267
278
280 """gets the absolute path to a template file"""
281 if templatepath is not None and self.usetemplates and options.template:
282 if self.isarchive(options.template, 'template'):
283 return templatepath
284 elif not options.recursivetemplate:
285 return templatepath
286 else:
287 return os.path.join(options.template, templatepath)
288 else:
289 return None
290
298
300 """gets the absolute path to an output file"""
301 if self.isarchive(options.output, 'output'):
302 return outputpath
303 elif options.recursiveoutput and options.output:
304 return os.path.join(options.output, outputpath)
305 else:
306 return outputpath
307
312
323
325 """opens the templatearchive if not already open"""
326 if not self.usetemplates:
327 return
328 if options.template and self.isarchive(options.template, 'template') and not hasattr(options, "templatearchive"):
329 options.templatearchive = self.openarchive(options.template, 'template')
330
335
347
348 - def processfile(self, fileprocessor, options, fullinputpath, fulloutputpath, fulltemplatepath):
349 """run an invidividual conversion"""
350 if self.isarchive(options.output, 'output'):
351 inputfile = self.openinputfile(options, fullinputpath)
352
353 templatefile = self.opentemplatefile(options, fulltemplatepath)
354 outputfile = self.openoutputfile(options, fulloutputpath)
355 passthroughoptions = self.getpassthroughoptions(options)
356 if fileprocessor(inputfile, outputfile, templatefile, **passthroughoptions):
357 if not outputfile.isatty():
358 outputfile.close()
359 return True
360 else:
361 if fulloutputpath and os.path.isfile(fulloutputpath):
362 outputfile.close()
363 os.unlink(fulloutputpath)
364 return False
365 else:
366 return super(ArchiveConvertOptionParser, self).processfile(fileprocessor, options, fullinputpath, fulloutputpath, fulltemplatepath)
367
368 -def main(argv=None):
369 parser = ArchiveConvertOptionParser({}, description=__doc__)
370 parser.run(argv)
371