1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """Conflict finder for Gettext PO localization files
23
24 See: http://translate.sourceforge.net/wiki/toolkit/poconflicts for examples and
25 usage instructions
26 """
27
28 import sys
29 import os
30
31 from translate.storage import factory
32 from translate.storage import po
33 from translate.misc import optrecurse
34
35
37 """a specialized Option Parser for the conflict tool..."""
38
40 """parses the command line options, handling implicit input/output args"""
41 (options, args) = optrecurse.optparse.OptionParser.parse_args(self, args, values)
42
43 if args and not options.input:
44 if not options.output:
45 options.input = args[:-1]
46 args = args[-1:]
47 else:
48 options.input = args
49 args = []
50 if args and not options.output:
51 options.output = args[-1]
52 args = args[:-1]
53 if not options.output:
54 self.error("output file is required")
55 if args:
56 self.error("You have used an invalid combination of --input, --output and freestanding args")
57 if isinstance(options.input, list) and len(options.input) == 1:
58 options.input = options.input[0]
59 return (options, args)
60
62 """sets the usage string - if usage not given, uses getusagestring for each option"""
63 if usage is None:
64 self.usage = "%prog " + " ".join([self.getusagestring(option) for option in self.option_list]) + \
65 "\n input directory is searched for PO files, PO files with name of conflicting string are output in output directory"
66 else:
67 super(ConflictOptionParser, self).set_usage(usage)
68
76
78 """recurse through directories and process files"""
79 if self.isrecursive(options.input, 'input') and getattr(options, "allowrecursiveinput", True):
80 if not self.isrecursive(options.output, 'output'):
81 try:
82 self.warning("Output directory does not exist. Attempting to create")
83 os.mkdir(options.output)
84 except:
85 self.error(optrecurse.optparse.OptionValueError("Output directory does not exist, attempt to create failed"))
86 if isinstance(options.input, list):
87 inputfiles = self.recurseinputfilelist(options)
88 else:
89 inputfiles = self.recurseinputfiles(options)
90 else:
91 if options.input:
92 inputfiles = [os.path.basename(options.input)]
93 options.input = os.path.dirname(options.input)
94 else:
95 inputfiles = [options.input]
96 self.textmap = {}
97 self.initprogressbar(inputfiles, options)
98 for inputpath in inputfiles:
99 fullinputpath = self.getfullinputpath(options, inputpath)
100 try:
101 success = self.processfile(None, options, fullinputpath)
102 except Exception, error:
103 if isinstance(error, KeyboardInterrupt):
104 raise
105 self.warning("Error processing: input %s" % (fullinputpath), options, sys.exc_info())
106 success = False
107 self.reportprogress(inputpath, success)
108 del self.progressbar
109 self.buildconflictmap()
110 self.outputconflicts(options)
111
112 - def clean(self, string, options):
113 """returns the cleaned string that contains the text to be matched"""
114 if options.ignorecase:
115 string = string.lower()
116 for accelerator in options.accelchars:
117 string = string.replace(accelerator, "")
118 string = string.strip()
119 return string
120
121 - def processfile(self, fileprocessor, options, fullinputpath):
137
138 - def flatten(self, text, joinchar):
139 """flattens text to just be words"""
140 flattext = ""
141 for c in text:
142 if c.isalnum():
143 flattext += c
144 elif flattext[-1:].isalnum():
145 flattext += joinchar
146 return flattext.rstrip(joinchar)
147
159
161 """saves the result of the conflict match"""
162 print "%d/%d different strings have conflicts" % (len(self.conflictmap), len(self.textmap))
163 reducedmap = {}
164 for source, translations in self.conflictmap.iteritems():
165 words = source.split()
166 words.sort(lambda x, y: cmp(len(x), len(y)))
167 source = words[-1]
168 reducedmap.setdefault(source, []).extend(translations)
169
170 plurals = {}
171 for word in reducedmap:
172 if word + "s" in reducedmap:
173 plurals[word] = word + "s"
174 for word, pluralword in plurals.iteritems():
175 reducedmap[word].extend(reducedmap.pop(pluralword))
176 for source, translations in reducedmap.iteritems():
177 flatsource = self.flatten(source, "-")
178 fulloutputpath = os.path.join(options.output, flatsource + os.extsep + "po")
179 conflictfile = po.pofile()
180 for target, unit, filename in translations:
181 unit.othercomments.append("# (poconflicts) %s\n" % filename)
182 conflictfile.units.append(unit)
183 open(fulloutputpath, "w").write(str(conflictfile))
184
185
187 formats = {"po": ("po", None), None: ("po", None)}
188 parser = ConflictOptionParser(formats)
189 parser.add_option("-I", "--ignore-case", dest="ignorecase",
190 action="store_true", default=False, help="ignore case distinctions")
191 parser.add_option("-v", "--invert", dest="invert",
192 action="store_true", default=False, help="invert the conflicts thus extracting conflicting destination words")
193 parser.add_option("", "--accelerator", dest="accelchars", default="",
194 metavar="ACCELERATORS", help="ignores the given accelerator characters when matching")
195 parser.set_usage()
196 parser.description = __doc__
197 parser.run()
198
199
200 if __name__ == '__main__':
201 main()
202