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-2010 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
49 -class podebug:
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
59 - def apply_to_translatables(self, string, func):
60 """Applies func to all translatable strings in string.""" 61 string.map( 62 lambda e: e.apply_to_strings(func), 63 lambda e: e.isleaf() and e.istranslatable)
64
65 - def rewritelist(cls):
66 return [rewrite.replace("rewrite_", "") for rewrite in dir(cls) if rewrite.startswith("rewrite_")]
67 rewritelist = classmethod(rewritelist) 68
69 - def _rewrite_prepend_append(self, string, prepend, append=None):
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 # Try and remove the last character from the tree 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
88 - def rewrite_xxx(self, string):
89 return self._rewrite_prepend_append(string, u"xxx")
90
91 - def rewrite_bracket(self, string):
92 return self._rewrite_prepend_append(string, u"[", u"]")
93
94 - def rewrite_en(self, string):
95 if not isinstance(string, StringElem): 96 string = StringElem(string) 97 return string
98
99 - def rewrite_blank(self, string):
100 return StringElem(u"")
101
102 - def rewrite_chef(self, string):
103 """Rewrite using Mock Swedish as made famous by Monty Python""" 104 if not isinstance(string, StringElem): 105 string = StringElem(string) 106 # From Dive into Python which itself got it elsewhere 107 # http://www.renderx.com/demos/examples/diveintopython.pdf 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
141 - def rewrite_unicode(self, string):
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 # Brackets should be swapped if the string will be reversed in memory. 161 # If a right-to-left override is used, the brackets should be 162 # unchanged. 163 #Some alternatives: 164 # D: ᗡ◖ 165 # K: Ж⋊Ӽ 166 # @: Ҩ - Seems only related in Dejavu Sans 167 # Q: Ὄ Ό Ὀ Ὃ Ὄ Ṑ Ò Ỏ 168 # _: ‾ - left out for now for the sake of GTK accelerators 169
170 - def rewrite_flipped(self, string):
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 # To reverse instead of using the RTL override: 184 #return u''.join(reversed([transpose(c) for c in s])) 185 self.apply_to_translatables(string, transformer) 186 return string 187
188 - def ignorelist(cls):
189 return [ignore.replace("ignore_", "") for ignore in dir(cls) if ignore.startswith("ignore_")]
190 ignorelist = classmethod(ignorelist) 191
192 - def ignore_openoffice(self, unit):
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
202 - def ignore_mozilla(self, unit):
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
215 - def ignore_gtk(self, unit):
216 if unit.source == "default:LTR": 217 return True 218 return False
219
220 - def ignore_kde(self, unit):
221 if unit.source == "LTR": 222 return True 223 return False
224
225 - def convertunit(self, unit, prefix):
226 if self.ignorefunc: 227 if self.ignorefunc(unit): 228 return unit 229 if prefix.find("@hash_placeholder@") != -1: 230 if unit.getlocations(): 231 hashable = unit.getlocations()[0] 232 else: 233 hashable = unit.source 234 prefix = prefix.replace("@hash_placeholder@", hash.md5_f(hashable).hexdigest()[:self.hash_len]) 235 rich_source = unit.rich_source 236 if not isinstance(rich_source, StringElem): 237 rich_source = [rich_parse(string, podebug_parsers) for string in rich_source] 238 if self.rewritefunc: 239 rewritten = [self.rewritefunc(string) for string in rich_source] 240 if rewritten: 241 unit.rich_target = rewritten 242 elif not unit.istranslated(): 243 unit.rich_target = unit.rich_source 244 unit.rich_target = add_prefix(prefix, unit.rich_target) 245 return unit
246
247 - def convertstore(self, store):
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
287 - def shrinkfilename(self, filename):
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 # note that templatefile is not used, but it is required by the converter... 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
316 -def main():
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 # TODO: add documentation on format strings... 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