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 def str_len(x):
165 return len(x)
166
167 for source, translations in self.conflictmap.iteritems():
168 words = source.split()
169 words.sort(key=str_len)
170 source = words[-1]
171 reducedmap.setdefault(source, []).extend(translations)
172
173 plurals = {}
174 for word in reducedmap:
175 if word + "s" in reducedmap:
176 plurals[word] = word + "s"
177 for word, pluralword in plurals.iteritems():
178 reducedmap[word].extend(reducedmap.pop(pluralword))
179 for source, translations in reducedmap.iteritems():
180 flatsource = self.flatten(source, "-")
181 fulloutputpath = os.path.join(options.output, flatsource + os.extsep + "po")
182 conflictfile = po.pofile()
183 for target, unit, filename in translations:
184 unit.othercomments.append("# (poconflicts) %s\n" % filename)
185 conflictfile.units.append(unit)
186 open(fulloutputpath, "w").write(str(conflictfile))
187
188
190 formats = {"po": ("po", None), None: ("po", None)}
191 parser = ConflictOptionParser(formats)
192 parser.add_option("-I", "--ignore-case", dest="ignorecase",
193 action="store_true", default=False, help="ignore case distinctions")
194 parser.add_option("-v", "--invert", dest="invert",
195 action="store_true", default=False, help="invert the conflicts thus extracting conflicting destination words")
196 parser.add_option("", "--accelerator", dest="accelchars", default="",
197 metavar="ACCELERATORS", help="ignores the given accelerator characters when matching")
198 parser.set_usage()
199 parser.description = __doc__
200 parser.run()
201
202
203 if __name__ == '__main__':
204 main()
205