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'):
198
199 - def isarchive(self, fileoption, filepurpose='input'):
200 """returns whether the file option is an archive file"""
201 if not isinstance(fileoption, (str, unicode)):
202 return False
203 mustexist = (filepurpose != 'output')
204 if mustexist and not os.path.isfile(fileoption):
205 return False
206 fileext = self.splitext(fileoption)[1]
207
208 return self.getarchiveclass(fileext, filepurpose, os.path.isdir(fileoption)) is not None
209
211 """returns the archiveclass for the given fileext and filepurpose"""
212 archiveclass = self.archiveformats.get(fileext, None)
213 if archiveclass is not None:
214 return archiveclass
215 archiveclass = self.archiveformats.get((fileext, filepurpose), None)
216 if archiveclass is not None:
217 return archiveclass
218 if not isdir:
219 archiveclass = self.archiveformats.get(None, None)
220 if archiveclass is not None:
221 return archiveclass
222 archiveclass = self.archiveformats.get((None, filepurpose), None)
223 if archiveclass is not None:
224 return archiveclass
225 return None
226
227 - def openarchive(self, archivefilename, filepurpose, **kwargs):
228 """creates an archive object for the given file"""
229 archiveext = self.splitext(archivefilename)[1]
230 archiveclass = self.getarchiveclass(archiveext, filepurpose, os.path.isdir(archivefilename))
231 archiveoptions = self.archiveoptions.copy()
232 archiveoptions.update(kwargs)
233 return archiveclass(archivefilename, **archiveoptions)
234
242
244 """recurse through archive files and convert files"""
245 inputfiles = []
246 for inputpath in options.inputarchive:
247 if self.isexcluded(options, inputpath):
248 continue
249 top, name = os.path.split(inputpath)
250 if not self.isvalidinputname(options, name):
251 continue
252 inputfiles.append(inputpath)
253 return inputfiles
254
261
268
279
281 """gets the absolute path to a template file"""
282 if templatepath is not None and self.usetemplates and options.template:
283 if self.isarchive(options.template, 'template'):
284 return templatepath
285 elif not options.recursivetemplate:
286 return templatepath
287 else:
288 return os.path.join(options.template, templatepath)
289 else:
290 return None
291
299
301 """gets the absolute path to an output file"""
302 if self.isarchive(options.output, 'output'):
303 return outputpath
304 elif options.recursiveoutput and options.output:
305 return os.path.join(options.output, outputpath)
306 else:
307 return outputpath
308
313
324
326 """opens the templatearchive if not already open"""
327 if not self.usetemplates:
328 return
329 if options.template and self.isarchive(options.template, 'template') and not hasattr(options, "templatearchive"):
330 options.templatearchive = self.openarchive(options.template, 'template')
331
336
348
349 - def processfile(self, fileprocessor, options, fullinputpath, fulloutputpath, fulltemplatepath):
350 """run an invidividual conversion"""
351 if self.isarchive(options.output, 'output'):
352 inputfile = self.openinputfile(options, fullinputpath)
353
354 templatefile = self.opentemplatefile(options, fulltemplatepath)
355 outputfile = self.openoutputfile(options, fulloutputpath)
356 passthroughoptions = self.getpassthroughoptions(options)
357 if fileprocessor(inputfile, outputfile, templatefile, **passthroughoptions):
358 if not outputfile.isatty():
359 outputfile.close()
360 return True
361 else:
362 if fulloutputpath and os.path.isfile(fulloutputpath):
363 outputfile.close()
364 os.unlink(fulloutputpath)
365 return False
366 else:
367 return super(ArchiveConvertOptionParser, self).processfile(fileprocessor, options, fullinputpath, fulloutputpath, fulltemplatepath)
368
369 -def main(argv=None):
370 parser = ArchiveConvertOptionParser({}, description=__doc__)
371 parser.run(argv)
372