Package translate :: Package tools :: Module podebug
[hide private]
[frames] | no frames]

Source Code for Module translate.tools.podebug

  1  #!/usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3  # 
  4  # Copyright 2004-2006,2008-2009 Zuza Software Foundation 
  5  # 
  6  # This file is part of the Translate Toolkit. 
  7  # 
  8  # This program is free software; you can redistribute it and/or modify 
  9  # it under the terms of the GNU General Public License as published by 
 10  # the Free Software Foundation; either version 2 of the License, or 
 11  # (at your option) any later version. 
 12  # 
 13  # This program is distributed in the hope that it will be useful, 
 14  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 16  # GNU General Public License for more details. 
 17  # 
 18  # You should have received a copy of the GNU General Public License 
 19  # along with this program; if not, see <http://www.gnu.org/licenses/>. 
 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   
37 -def add_prefix(prefix, stringelems):
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 -class podebug:
49 - def __init__(self, format=None, rewritestyle=None, ignoreoption=None):
50 if format is None: 51 self.format = "" 52 else: 53 self.format = format 54 self.rewritefunc = getattr(self, "rewrite_%s" % rewritestyle, None) 55 self.ignorefunc = getattr(self, "ignore_%s" % ignoreoption, None)
56
57 - def apply_to_translatables(self, string, func):
58 """Applies func to all translatable strings in string.""" 59 string.map( 60 lambda e: e.apply_to_strings(func), 61 lambda e: e.isleaf() and e.istranslatable 62 )
63
64 - def rewritelist(cls):
65 return [rewrite.replace("rewrite_", "") for rewrite in dir(cls) if rewrite.startswith("rewrite_")]
66 rewritelist = classmethod(rewritelist) 67
68 - def _rewrite_prepend_append(self, string, prepend, append=None):
69 if append is None: 70 append = prepend 71 if not isinstance(string, StringElem): 72 string = StringElem(string) 73 string.sub.insert(0, prepend) 74 if unicode(string).endswith(u'\n'): 75 # Try and remove the last character from the tree 76 try: 77 lastnode = string.flatten()[-1] 78 if isinstance(lastnode.sub[-1], unicode): 79 lastnode.sub[-1] = lastnode.sub[-1].rstrip(u'\n') 80 except IndexError: 81 pass 82 string.sub.append(append + u'\n') 83 else: 84 string.sub.append(append) 85 return string
86
87 - def rewrite_xxx(self, string):
88 return self._rewrite_prepend_append(string, u"xxx")
89
90 - def rewrite_bracket(self, string):
91 return self._rewrite_prepend_append(string, u"[", u"]")
92
93 - def rewrite_en(self, string):
94 if not isinstance(string, StringElem): 95 string = StringElem(string) 96 return string
97
98 - def rewrite_blank(self, string):
99 return StringElem(u"")
100
101 - def rewrite_chef(self, string):
102 """Rewrite using Mock Swedish as made famous by Monty Python""" 103 if not isinstance(string, StringElem): 104 string = StringElem(string) 105 # From Dive into Python which itself got it elsewhere 106 # http://www.renderx.com/demos/examples/diveintopython.pdf 107 subs = ( 108 (r'a([nu])', r'u\1'), 109 (r'A([nu])', r'U\1'), 110 (r'a\B', r'e'), 111 (r'A\B', r'E'), 112 (r'en\b', r'ee'), 113 (r'\Bew', r'oo'), 114 (r'\Be\b', r'e-a'), 115 (r'\be', r'i'), 116 (r'\bE', r'I'), 117 (r'\Bf', r'ff'), 118 (r'\Bir', r'ur'), 119 (r'(\w*?)i(\w*?)$', r'\1ee\2'), 120 (r'\bow', r'oo'), 121 (r'\bo', r'oo'), 122 (r'\bO', r'Oo'), 123 (r'the', r'zee'), 124 (r'The', r'Zee'), 125 (r'th\b', r't'), 126 (r'\Btion', r'shun'), 127 (r'\Bu', r'oo'), 128 (r'\BU', r'Oo'), 129 (r'v', r'f'), 130 (r'V', r'F'), 131 (r'w', r'w'), 132 (r'W', r'W'), 133 (r'([a-z])[.]', r'\1. Bork Bork Bork!')) 134 for a, b in subs: 135 self.apply_to_translatables(string, lambda s: re.sub(a, b, s)) 136 return string
137 138 REWRITE_UNICODE_MAP = u"ȦƁƇḒḖƑƓĦĪĴĶĿḾȠǾƤɊŘŞŦŬṼẆẊẎẐ" + u"[\\]^_`" + u"ȧƀƈḓḗƒɠħīĵķŀḿƞǿƥɋřşŧŭṽẇẋẏẑ"
139 - def rewrite_unicode(self, string):
140 """Convert to Unicode characters that look like the source string""" 141 if not isinstance(string, StringElem): 142 string = StringElem(string) 143 def transpose(char): 144 loc = ord(char)-65 145 if loc < 0 or loc > 56: 146 return char 147 return self.REWRITE_UNICODE_MAP[loc]
148 def transformer(s): 149 return ''.join([transpose(c) for c in s])
150 self.apply_to_translatables(string, transformer) 151 return string 152 153 REWRITE_FLIPPED_MAP = u"¡„#$%⅋,()⁎+´-·/012Ɛᔭ59Ƚ86:;<=>?@" + \ 154 u"∀ԐↃᗡƎℲ⅁HIſӼ⅂WNOԀÒᴚS⊥∩ɅMX⅄Z" + u"[\\]ᵥ_," + \ 155 u"ɐqɔpǝɟƃɥıɾʞʅɯuodbɹsʇnʌʍxʎz" 156 # Brackets should be swapped if the string will be reversed in memory. 157 # If a right-to-left override is used, the brackets should be 158 # unchanged. 159 #Some alternatives: 160 # D: ᗡ◖ 161 # K: Ж⋊Ӽ 162 # @: Ҩ - Seems only related in Dejavu Sans 163 # Q: Ὄ Ό Ὀ Ὃ Ὄ Ṑ Ò Ỏ 164 # _: ‾ - left out for now for the sake of GTK accelerators
165 - def rewrite_flipped(self, string):
166 """Convert the string to look flipped upside down.""" 167 if not isinstance(string, StringElem): 168 string = StringElem(string) 169 def transpose(char): 170 loc = ord(char)-33 171 if loc < 0 or loc > 89: 172 return char 173 return self.REWRITE_FLIPPED_MAP[loc]
174 def transformer(s): 175 return u"\u202e" + u''.join([transpose(c) for c in s]) 176 # To reverse instead of using the RTL override: 177 #return u''.join(reversed([transpose(c) for c in s])) 178 self.apply_to_translatables(string, transformer) 179 return string 180
181 - def ignorelist(cls):
182 return [ignore.replace("ignore_", "") for ignore in dir(cls) if ignore.startswith("ignore_")]
183 ignorelist = classmethod(ignorelist) 184
185 - def ignore_openoffice(self, unit):
186 for location in unit.getlocations(): 187 if location.startswith("Common.xcu#..Common.View.Localisation"): 188 return True 189 elif location.startswith("profile.lng#STR_DIR_MENU_NEW_"): 190 return True 191 elif location.startswith("profile.lng#STR_DIR_MENU_WIZARD_"): 192 return True 193 return False
194
195 - def ignore_mozilla(self, unit):
196 locations = unit.getlocations() 197 if len(locations) == 1 and locations[0].lower().endswith(".accesskey"): 198 return True 199 for location in locations: 200 if dtd2po.is_css_entity(location): 201 return True 202 if location in ["brandShortName", "brandFullName", "vendorShortName"]: 203 return True 204 if location.lower().endswith(".commandkey") or location.endswith(".key"): 205 return True 206 return False
207
208 - def ignore_gtk(self, unit):
209 if unit.source == "default:LTR": 210 return True 211 return False
212
213 - def ignore_kde(self, unit):
214 if unit.source == "LTR": 215 return True 216 return False
217
218 - def convertunit(self, unit, prefix):
219 if self.ignorefunc: 220 if self.ignorefunc(unit): 221 return unit 222 if prefix.find("@hash_placeholder@") != -1: 223 if unit.getlocations(): 224 hashable = unit.getlocations()[0] 225 else: 226 hashable = unit.source 227 prefix = prefix.replace("@hash_placeholder@", hash.md5_f(hashable).hexdigest()[:self.hash_len]) 228 rich_source = unit.rich_source 229 if not isinstance(rich_source, StringElem): 230 rich_source = [rich_parse(string, podebug_parsers) for string in rich_source] 231 if self.rewritefunc: 232 rewritten = [self.rewritefunc(string) for string in rich_source] 233 if rewritten: 234 unit.rich_target = rewritten 235 elif not unit.istranslated(): 236 unit.rich_target = unit.rich_source 237 unit.rich_target = add_prefix(prefix, unit.rich_target) 238 return unit
239
240 - def convertstore(self, store):
241 filename = self.shrinkfilename(store.filename) 242 prefix = self.format 243 for formatstr in re.findall("%[0-9c]*[sfFbBdh]", self.format): 244 if formatstr.endswith("s"): 245 formatted = self.shrinkfilename(store.filename) 246 elif formatstr.endswith("f"): 247 formatted = store.filename 248 formatted = os.path.splitext(formatted)[0] 249 elif formatstr.endswith("F"): 250 formatted = store.filename 251 elif formatstr.endswith("b"): 252 formatted = os.path.basename(store.filename) 253 formatted = os.path.splitext(formatted)[0] 254 elif formatstr.endswith("B"): 255 formatted = os.path.basename(store.filename) 256 elif formatstr.endswith("d"): 257 formatted = os.path.dirname(store.filename) 258 elif formatstr.endswith("h"): 259 try: 260 self.hash_len = int(filter(str.isdigit, formatstr[1:-1])) 261 except ValueError: 262 self.hash_len = 4 263 formatted = "@hash_placeholder@" 264 else: 265 continue 266 formatoptions = formatstr[1:-1] 267 if formatoptions and not formatstr.endswith("h"): 268 if "c" in formatoptions and formatted: 269 formatted = formatted[0] + filter(lambda x: x.lower() not in "aeiou", formatted[1:]) 270 length = filter(str.isdigit, formatoptions) 271 if length: 272 formatted = formatted[:int(length)] 273 prefix = prefix.replace(formatstr, formatted) 274 for unit in store.units: 275 if not unit.istranslatable(): 276 continue 277 unit = self.convertunit(unit, prefix) 278 return store
279
280 - def shrinkfilename(self, filename):
281 if filename.startswith("." + os.sep): 282 filename = filename.replace("." + os.sep, "", 1) 283 dirname = os.path.dirname(filename) 284 dirparts = dirname.split(os.sep) 285 if not dirparts: 286 dirshrunk = "" 287 else: 288 dirshrunk = dirparts[0][:4] + "-" 289 if len(dirparts) > 1: 290 dirshrunk += "".join([dirpart[0] for dirpart in dirparts[1:]]) + "-" 291 baseshrunk = os.path.basename(filename)[:4] 292 if "." in baseshrunk: 293 baseshrunk = baseshrunk[:baseshrunk.find(".")] 294 return dirshrunk + baseshrunk
295
296 -def convertpo(inputfile, outputfile, templatefile, format=None, rewritestyle=None, ignoreoption=None):
297 """Reads in inputfile, changes it to have debug strings, writes to outputfile.""" 298 # note that templatefile is not used, but it is required by the converter... 299 inputstore = factory.getobject(inputfile) 300 if inputstore.isempty(): 301 return 0 302 convertor = podebug(format=format, rewritestyle=rewritestyle, ignoreoption=ignoreoption) 303 outputstore = convertor.convertstore(inputstore) 304 outputfile.write(str(outputstore)) 305 return 1
306
307 -def main():
308 from translate.convert import convert 309 formats = {"po":("po", convertpo), "pot":("po", convertpo), "xlf":("xlf", convertpo)} 310 parser = convert.ConvertOptionParser(formats, description=__doc__) 311 # TODO: add documentation on format strings... 312 parser.add_option("-f", "--format", dest="format", default="", 313 help="specify format string") 314 parser.add_option("", "--rewrite", dest="rewritestyle", 315 type="choice", choices=podebug.rewritelist(), metavar="STYLE", 316 help="the translation rewrite style: %s" % ", ".join(podebug.rewritelist())) 317 parser.add_option("", "--ignore", dest="ignoreoption", 318 type="choice", choices=podebug.ignorelist(), metavar="APPLICATION", 319 help="apply tagging ignore rules for the given application: %s" % ", ".join(podebug.ignorelist())) 320 parser.passthrough.append("format") 321 parser.passthrough.append("rewritestyle") 322 parser.passthrough.append("ignoreoption") 323 parser.run()
324 325 326 if __name__ == '__main__': 327 main() 328