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