1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 """Insert debug messages into XLIFF and Gettext PO localization files
22
23 See: http://translate.sourceforge.net/wiki/toolkit/podebug for examples and
24 usage instructions.
25 """
26
27 import os
28 import re
29
30 from translate.misc import hash
31 from translate.storage import factory
32 from translate.storage.placeables import StringElem, general
33 from translate.storage.placeables import parse as rich_parse
34 from translate.convert import dtd2po
35
36
38 for stringelem in stringelems:
39 for string in stringelem.flatten():
40 if len(string.sub) > 0:
41 string.sub[0] = prefix + string.sub[0]
42 return stringelems
43
44 podebug_parsers = general.parsers
45 podebug_parsers.remove(general.CapsPlaceable.parse)
46 podebug_parsers.remove(general.CamelCasePlaceable.parse)
47
48
50
51 - def __init__(self, format=None, rewritestyle=None, ignoreoption=None):
52 if format is None:
53 self.format = ""
54 else:
55 self.format = format
56 self.rewritefunc = getattr(self, "rewrite_%s" % rewritestyle, None)
57 self.ignorefunc = getattr(self, "ignore_%s" % ignoreoption, None)
58
64
66 return [rewrite.replace("rewrite_", "") for rewrite in dir(cls) if rewrite.startswith("rewrite_")]
67 rewritelist = classmethod(rewritelist)
68
70 if append is None:
71 append = prepend
72 if not isinstance(string, StringElem):
73 string = StringElem(string)
74 string.sub.insert(0, prepend)
75 if unicode(string).endswith(u'\n'):
76
77 try:
78 lastnode = string.flatten()[-1]
79 if isinstance(lastnode.sub[-1], unicode):
80 lastnode.sub[-1] = lastnode.sub[-1].rstrip(u'\n')
81 except IndexError:
82 pass
83 string.sub.append(append + u'\n')
84 else:
85 string.sub.append(append)
86 return string
87
90
93
98
101
103 """Rewrite using Mock Swedish as made famous by Monty Python"""
104 if not isinstance(string, StringElem):
105 string = StringElem(string)
106
107
108 subs = (
109 (r'a([nu])', r'u\1'),
110 (r'A([nu])', r'U\1'),
111 (r'a\B', r'e'),
112 (r'A\B', r'E'),
113 (r'en\b', r'ee'),
114 (r'\Bew', r'oo'),
115 (r'\Be\b', r'e-a'),
116 (r'\be', r'i'),
117 (r'\bE', r'I'),
118 (r'\Bf', r'ff'),
119 (r'\Bir', r'ur'),
120 (r'(\w*?)i(\w*?)$', r'\1ee\2'),
121 (r'\bow', r'oo'),
122 (r'\bo', r'oo'),
123 (r'\bO', r'Oo'),
124 (r'the', r'zee'),
125 (r'The', r'Zee'),
126 (r'th\b', r't'),
127 (r'\Btion', r'shun'),
128 (r'\Bu', r'oo'),
129 (r'\BU', r'Oo'),
130 (r'v', r'f'),
131 (r'V', r'F'),
132 (r'w', r'w'),
133 (r'W', r'W'),
134 (r'([a-z])[.]', r'\1. Bork Bork Bork!'))
135 for a, b in subs:
136 self.apply_to_translatables(string, lambda s: re.sub(a, b, s))
137 return string
138
139 REWRITE_UNICODE_MAP = u"ȦƁƇḒḖƑƓĦĪĴĶĿḾȠǾƤɊŘŞŦŬṼẆẊẎẐ" + u"[\\]^_`" + u"ȧƀƈḓḗƒɠħīĵķŀḿƞǿƥɋřşŧŭṽẇẋẏẑ"
140
142 """Convert to Unicode characters that look like the source string"""
143 if not isinstance(string, StringElem):
144 string = StringElem(string)
145
146 def transpose(char):
147 loc = ord(char)-65
148 if loc < 0 or loc > 56:
149 return char
150 return self.REWRITE_UNICODE_MAP[loc]
151
152 def transformer(s):
153 return ''.join([transpose(c) for c in s])
154 self.apply_to_translatables(string, transformer)
155 return string
156
157 REWRITE_FLIPPED_MAP = u"¡„#$%⅋,()⁎+´-˙/012Ɛᔭ59Ƚ86:;<=>¿@" + \
158 u"∀ԐↃᗡƎℲ⅁HIſӼ⅂WNOԀÒᴚS⊥∩ɅMX⅄Z" + u"[\\]ᵥ_," + \
159 u"ɐqɔpǝɟƃɥıɾʞʅɯuodbɹsʇnʌʍxʎz"
160
161
162
163
164
165
166
167
168
169
171 """Convert the string to look flipped upside down."""
172 if not isinstance(string, StringElem):
173 string = StringElem(string)
174
175 def transpose(char):
176 loc = ord(char)-33
177 if loc < 0 or loc > 89:
178 return char
179 return self.REWRITE_FLIPPED_MAP[loc]
180
181 def transformer(s):
182 return u"\u202e" + u''.join([transpose(c) for c in s])
183
184
185 self.apply_to_translatables(string, transformer)
186 return string
187
189 return [ignore.replace("ignore_", "") for ignore in dir(cls) if ignore.startswith("ignore_")]
190 ignorelist = classmethod(ignorelist)
191
193 for location in unit.getlocations():
194 if location.startswith("Common.xcu#..Common.View.Localisation"):
195 return True
196 elif location.startswith("profile.lng#STR_DIR_MENU_NEW_"):
197 return True
198 elif location.startswith("profile.lng#STR_DIR_MENU_WIZARD_"):
199 return True
200 return False
201
203 locations = unit.getlocations()
204 if len(locations) == 1 and locations[0].lower().endswith(".accesskey"):
205 return True
206 for location in locations:
207 if dtd2po.is_css_entity(location):
208 return True
209 if location in ["brandShortName", "brandFullName", "vendorShortName"]:
210 return True
211 if location.lower().endswith(".commandkey") or location.endswith(".key"):
212 return True
213 return False
214
216 if unit.source == "default:LTR":
217 return True
218 return False
219
221 if unit.source == "LTR":
222 return True
223 return False
224
246
248 filename = self.shrinkfilename(store.filename)
249 prefix = self.format
250 for formatstr in re.findall("%[0-9c]*[sfFbBdh]", self.format):
251 if formatstr.endswith("s"):
252 formatted = self.shrinkfilename(store.filename)
253 elif formatstr.endswith("f"):
254 formatted = store.filename
255 formatted = os.path.splitext(formatted)[0]
256 elif formatstr.endswith("F"):
257 formatted = store.filename
258 elif formatstr.endswith("b"):
259 formatted = os.path.basename(store.filename)
260 formatted = os.path.splitext(formatted)[0]
261 elif formatstr.endswith("B"):
262 formatted = os.path.basename(store.filename)
263 elif formatstr.endswith("d"):
264 formatted = os.path.dirname(store.filename)
265 elif formatstr.endswith("h"):
266 try:
267 self.hash_len = int(filter(str.isdigit, formatstr[1:-1]))
268 except ValueError:
269 self.hash_len = 4
270 formatted = "@hash_placeholder@"
271 else:
272 continue
273 formatoptions = formatstr[1:-1]
274 if formatoptions and not formatstr.endswith("h"):
275 if "c" in formatoptions and formatted:
276 formatted = formatted[0] + filter(lambda x: x.lower() not in "aeiou", formatted[1:])
277 length = filter(str.isdigit, formatoptions)
278 if length:
279 formatted = formatted[:int(length)]
280 prefix = prefix.replace(formatstr, formatted)
281 for unit in store.units:
282 if not unit.istranslatable():
283 continue
284 unit = self.convertunit(unit, prefix)
285 return store
286
288 if filename.startswith("." + os.sep):
289 filename = filename.replace("." + os.sep, "", 1)
290 dirname = os.path.dirname(filename)
291 dirparts = dirname.split(os.sep)
292 if not dirparts:
293 dirshrunk = ""
294 else:
295 dirshrunk = dirparts[0][:4] + "-"
296 if len(dirparts) > 1:
297 dirshrunk += "".join([dirpart[0] for dirpart in dirparts[1:]]) + "-"
298 baseshrunk = os.path.basename(filename)[:4]
299 if "." in baseshrunk:
300 baseshrunk = baseshrunk[:baseshrunk.find(".")]
301 return dirshrunk + baseshrunk
302
303
304 -def convertpo(inputfile, outputfile, templatefile, format=None, rewritestyle=None, ignoreoption=None):
305 """Reads in inputfile, changes it to have debug strings, writes to outputfile."""
306
307 inputstore = factory.getobject(inputfile)
308 if inputstore.isempty():
309 return 0
310 convertor = podebug(format=format, rewritestyle=rewritestyle, ignoreoption=ignoreoption)
311 outputstore = convertor.convertstore(inputstore)
312 outputfile.write(str(outputstore))
313 return 1
314
315
317 from translate.convert import convert
318 formats = {
319 "po": ("po", convertpo), "pot": ("po", convertpo),
320 "xlf": ("xlf", convertpo),
321 "tmx": ("tmx", convertpo),
322 }
323 parser = convert.ConvertOptionParser(formats, description=__doc__)
324
325 parser.add_option("-f", "--format", dest="format", default="",
326 help="specify format string")
327 parser.add_option("", "--rewrite", dest="rewritestyle",
328 type="choice", choices=podebug.rewritelist(), metavar="STYLE",
329 help="the translation rewrite style: %s" % ", ".join(podebug.rewritelist()))
330 parser.add_option("", "--ignore", dest="ignoreoption",
331 type="choice", choices=podebug.ignorelist(), metavar="APPLICATION",
332 help="apply tagging ignore rules for the given application: %s" % ", ".join(podebug.ignorelist()))
333 parser.passthrough.append("format")
334 parser.passthrough.append("rewritestyle")
335 parser.passthrough.append("ignoreoption")
336 parser.run()
337
338
339 if __name__ == '__main__':
340 main()
341