Package translate :: Package storage :: Module cpo
[hide private]
[frames] | no frames]

Source Code for Module translate.storage.cpo

  1  #!/usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3  #  
  4  # Copyright 2002-2007 Zuza Software Foundation 
  5  #  
  6  # This file is part of translate. 
  7  # 
  8  # translate 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  # translate 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 translate; if not, write to the Free Software 
 20  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 21   
 22  """Classes that hold units of .po files (pounit) or entire files (pofile). 
 23   
 24  Gettext-style .po (or .pot) files are used in translations for KDE, GNOME and 
 25  many other projects. 
 26   
 27  This uses libgettextpo from the gettext package. Any version before 0.17 will 
 28  at least cause some subtle bugs or may not work at all. Developers might want 
 29  to have a look at gettext-tools/libgettextpo/gettext-po.h from the gettext 
 30  package for the public API of the library. 
 31  """ 
 32   
 33  from translate.misc.multistring import multistring 
 34  from translate.storage import pocommon 
 35  from translate.misc import quote 
 36  from translate.lang import data 
 37  from ctypes import * 
 38  import ctypes.util 
 39  try: 
 40      import cStringIO as StringIO 
 41  except ImportError: 
 42      import StringIO 
 43  import os 
 44  import pypo 
 45  import re 
 46  import sys 
 47  import tempfile 
 48   
 49  lsep = " " 
 50  """Seperator for #: entries""" 
 51   
 52  STRING = c_char_p 
 53   
 54  # Structures 
55 -class po_message(Structure):
56 _fields_ = []
57 58 # Function prototypes 59 xerror_prototype = CFUNCTYPE(None, c_int, POINTER(po_message), STRING, c_uint, c_uint, c_int, STRING) 60 xerror2_prototype = CFUNCTYPE(None, c_int, POINTER(po_message), STRING, c_uint, c_uint, c_int, STRING, POINTER(po_message), STRING, c_uint, c_uint, c_int, STRING) 61 62 63 # Structures (error handler)
64 -class po_xerror_handler(Structure):
65 _fields_ = [('xerror', xerror_prototype), 66 ('xerror2', xerror2_prototype)]
67
68 -class po_error_handler(Structure):
69 _fields_ = [ 70 ('error', CFUNCTYPE(None, c_int, c_int, STRING)), 71 ('error_at_line', CFUNCTYPE(None, c_int, c_int, STRING, c_uint, STRING)), 72 ('multiline_warning', CFUNCTYPE(None, STRING, STRING)), 73 ('multiline_error', CFUNCTYPE(None, STRING, STRING)), 74 ]
75 76 # Callback functions for po_xerror_handler
77 -def xerror_cb(severity, message, filename, lineno, column, multilint_p, message_text):
78 print >> sys.stderr, "xerror_cb", severity, message, filename, lineno, column, multilint_p, message_text 79 if severity >= 1: 80 raise ValueError(message_text)
81
82 -def xerror2_cb(severity, message1, filename1, lineno1, column1, multiline_p1, message_text1, message2, filename2, lineno2, column2, multiline_p2, message_text2):
83 print >> sys.stderr, "xerror2_cb", severity, message1, filename1, lineno1, column1, multiline_p1, message_text1, message2, filename2, lineno2, column2, multiline_p2, message_text2 84 if severity >= 1: 85 raise ValueError(message_text1)
86 87 88 89 # Load libgettextpo 90 gpo = None 91 # 'gettextpo' is recognised on Unix, while only 'libgettextpo' is recognised on 92 # windows. Therefore we test both. 93 names = ['gettextpo', 'libgettextpo'] 94 for name in names: 95 lib_location = ctypes.util.find_library(name) 96 if lib_location: 97 gpo = cdll.LoadLibrary(lib_location) 98 if gpo: 99 break 100 else: 101 # Now we are getting desperate, so let's guess a unix type DLL that might 102 # be in LD_LIBRARY_PATH or loaded with LD_PRELOAD 103 try: 104 gpo = cdll.LoadLibrary('libgettextpo.so') 105 except OSError, e: 106 raise ImportError("gettext PO library not found") 107 108 # Setup return and paramater types 109 # File access 110 gpo.po_file_read_v3.argtypes = [STRING, POINTER(po_xerror_handler)] 111 gpo.po_file_write_v2.argtypes = [c_int, STRING, POINTER(po_xerror_handler)] 112 gpo.po_file_write_v2.retype = c_int 113 114 # Header 115 gpo.po_file_domain_header.restype = STRING 116 gpo.po_header_field.restype = STRING 117 gpo.po_header_field.argtypes = [STRING, STRING] 118 119 # Locations (filepos) 120 gpo.po_filepos_file.restype = STRING 121 gpo.po_message_filepos.restype = c_int 122 gpo.po_message_filepos.argtypes = [c_int, c_int] 123 gpo.po_message_add_filepos.argtypes = [c_int, STRING, c_int] 124 125 # Message (get methods) 126 gpo.po_message_comments.restype = STRING 127 gpo.po_message_extracted_comments.restype = STRING 128 gpo.po_message_prev_msgctxt.restype = STRING 129 gpo.po_message_prev_msgid.restype = STRING 130 gpo.po_message_prev_msgid_plural.restype = STRING 131 gpo.po_message_is_format.restype = c_int 132 gpo.po_message_is_format.argtypes = [c_int, STRING] 133 gpo.po_message_set_format.argtypes = [c_int, STRING, c_int] 134 gpo.po_message_msgctxt.restype = STRING 135 gpo.po_message_msgid.restype = STRING 136 gpo.po_message_msgid_plural.restype = STRING 137 gpo.po_message_msgstr.restype = STRING 138 gpo.po_message_msgstr_plural.restype = STRING 139 140 # Message (set methods) 141 gpo.po_message_set_comments.argtypes = [c_int, STRING] 142 gpo.po_message_set_extracted_comments.argtypes = [c_int, STRING] 143 gpo.po_message_set_fuzzy.argtypes = [c_int, c_int] 144 gpo.po_message_set_msgctxt.argtypes = [c_int, STRING] 145 146 # Setup the po_xerror_handler 147 xerror_handler = po_xerror_handler() 148 xerror_handler.xerror = xerror_prototype(xerror_cb) 149 xerror_handler.xerror2 = xerror2_prototype(xerror2_cb) 150
151 -def escapeforpo(text):
152 return pypo.escapeforpo(text)
153
154 -def quoteforpo(text):
155 return pypo.quoteforpo(text)
156
157 -def unquotefrompo(postr):
158 return pypo.unquotefrompo(postr)
159
160 -def encodingToUse(encoding):
161 return pypo.encodingToUse(encoding)
162
163 -def get_libgettextpo_version():
164 """Returns the libgettextpo version 165 166 @rtype: three-value tuple 167 @return: libgettextpo version in the following format:: 168 (major version, minor version, subminor version) 169 """ 170 libversion = c_long.in_dll(gpo, 'libgettextpo_version') 171 major = libversion.value >> 16 172 minor = libversion.value >> 8 173 subminor = libversion.value - (major << 16) - (minor << 8) 174 return major, minor, subminor
175 176
177 -class pounit(pocommon.pounit):
178 - def __init__(self, source=None, encoding='utf-8', gpo_message=None):
179 self._rich_source = None 180 self._rich_target = None 181 self._encoding = encoding 182 if not gpo_message: 183 self._gpo_message = gpo.po_message_create() 184 if source or source == "": 185 self.source = source 186 self.target = "" 187 elif gpo_message: 188 self._gpo_message = gpo_message
189
190 - def setmsgid_plural(self, msgid_plural):
191 if isinstance(msgid_plural, list): 192 msgid_plural = "".join(msgid_plural) 193 gpo.po_message_set_msgid_plural(self._gpo_message, msgid_plural)
194 msgid_plural = property(None, setmsgid_plural) 195
196 - def getsource(self):
197 def remove_msgid_comments(text): 198 if not text: 199 return text 200 if text.startswith("_:"): 201 remainder = re.search(r"_: .*\n(.*)", text) 202 if remainder: 203 return remainder.group(1) 204 else: 205 return u"" 206 else: 207 return text
208 singular = remove_msgid_comments(gpo.po_message_msgid(self._gpo_message).decode(self._encoding)) 209 if singular: 210 if self.hasplural(): 211 multi = multistring(singular, self._encoding) 212 pluralform = gpo.po_message_msgid_plural(self._gpo_message).decode(self._encoding) 213 multi.strings.append(pluralform) 214 return multi 215 else: 216 return singular 217 else: 218 return u"" 219
220 - def setsource(self, source):
221 if isinstance(source, multistring): 222 source = source.strings 223 if isinstance(source, unicode): 224 source = source.encode(self._encoding) 225 if isinstance(source, list): 226 gpo.po_message_set_msgid(self._gpo_message, source[0].encode(self._encoding)) 227 if len(source) > 1: 228 gpo.po_message_set_msgid_plural(self._gpo_message, source[1].encode(self._encoding)) 229 else: 230 gpo.po_message_set_msgid(self._gpo_message, source) 231 gpo.po_message_set_msgid_plural(self._gpo_message, None)
232 233 source = property(getsource, setsource) 234
235 - def gettarget(self):
236 if self.hasplural(): 237 plurals = [] 238 nplural = 0 239 plural = gpo.po_message_msgstr_plural(self._gpo_message, nplural) 240 while plural: 241 plurals.append(plural.decode(self._encoding)) 242 nplural += 1 243 plural = gpo.po_message_msgstr_plural(self._gpo_message, nplural) 244 if plurals: 245 multi = multistring(plurals, encoding=self._encoding) 246 else: 247 multi = multistring(u"") 248 else: 249 multi = (gpo.po_message_msgstr(self._gpo_message) or "").decode(self._encoding) 250 return multi
251
252 - def settarget(self, target):
253 # for plural strings: convert 'target' into a list 254 if self.hasplural(): 255 if isinstance(target, multistring): 256 target = target.strings 257 elif isinstance(target, basestring): 258 target = [target] 259 # for non-plurals: check number of items in 'target' 260 elif isinstance(target,(dict, list)): 261 if len(target) == 1: 262 target = target[0] 263 else: 264 raise ValueError("po msgid element has no plural but msgstr has %d elements (%s)" % (len(target), target)) 265 # empty the previous list of messages 266 # TODO: the "pypo" implementation does not remove the previous items of 267 # the target, if self.target == target (essentially: comparing only 268 # the first item of a plural string with the single new string) 269 # Maybe this behaviour should be unified. 270 if isinstance(target, (dict, list)): 271 i = 0 272 message = gpo.po_message_msgstr_plural(self._gpo_message, i) 273 while message is not None: 274 gpo.po_message_set_msgstr_plural(self._gpo_message, i, None) 275 i += 1 276 message = gpo.po_message_msgstr_plural(self._gpo_message, i) 277 # add the items of a list 278 if isinstance(target, list): 279 for i in range(len(target)): 280 targetstring = target[i] 281 if isinstance(targetstring, unicode): 282 targetstring = targetstring.encode(self._encoding) 283 gpo.po_message_set_msgstr_plural(self._gpo_message, i, targetstring) 284 # add the values of a dict 285 elif isinstance(target, dict): 286 for i, targetstring in enumerate(target.itervalues()): 287 gpo.po_message_set_msgstr_plural(self._gpo_message, i, targetstring) 288 # add a single string 289 else: 290 if isinstance(target, unicode): 291 target = target.encode(self._encoding) 292 if target is None: 293 gpo.po_message_set_msgstr(self._gpo_message, "") 294 else: 295 gpo.po_message_set_msgstr(self._gpo_message, target)
296 target = property(gettarget, settarget) 297
298 - def getid(self):
299 """The unique identifier for this unit according to the convensions in 300 .mo files.""" 301 id = gpo.po_message_msgid(self._gpo_message) 302 # Gettext does not consider the plural to determine duplicates, only 303 # the msgid. For generation of .mo files, we might want to use this 304 # code to generate the entry for the hash table, but for now, it is 305 # commented out for conformance to gettext. 306 # plural = gpo.po_message_msgid_plural(self._gpo_message) 307 # if not plural is None: 308 # id = '%s\0%s' % (id, plural) 309 context = gpo.po_message_msgctxt(self._gpo_message) 310 if context: 311 id = "%s\04%s" % (context, id) 312 return id or ""
313
314 - def getnotes(self, origin=None):
315 if origin == None: 316 comments = gpo.po_message_comments(self._gpo_message) + \ 317 gpo.po_message_extracted_comments(self._gpo_message) 318 elif origin == "translator": 319 comments = gpo.po_message_comments(self._gpo_message) 320 elif origin in ["programmer", "developer", "source code"]: 321 comments = gpo.po_message_extracted_comments(self._gpo_message) 322 else: 323 raise ValueError("Comment type not valid") 324 325 if comments and get_libgettextpo_version() < (0, 17, 0): 326 comments = "\n".join([line.strip() for line in comments.split("\n")]) 327 # Let's drop the last newline 328 return comments[:-1].decode(self._encoding)
329
330 - def addnote(self, text, origin=None, position="append"):
331 # ignore empty strings and strings without non-space characters 332 if not (text and text.strip()): 333 return 334 text = data.forceunicode(text) 335 oldnotes = self.getnotes(origin) 336 newnotes = None 337 if oldnotes: 338 if position == "append": 339 newnotes = oldnotes + "\n" + text 340 elif position == "merge": 341 if oldnotes != text: 342 oldnoteslist = oldnotes.split("\n") 343 for newline in text.split("\n"): 344 newline = newline.rstrip() 345 # avoid duplicate comment lines (this might cause some problems) 346 if newline not in oldnotes or len(newline) < 5: 347 oldnoteslist.append(newline) 348 newnotes = "\n".join(oldnoteslist) 349 else: 350 newnotes = text + '\n' + oldnotes 351 else: 352 newnotes = "\n".join([line.rstrip() for line in text.split("\n")]) 353 354 if newnotes: 355 newlines = [] 356 needs_space = get_libgettextpo_version() < (0, 17, 0) 357 for line in newnotes.split("\n"): 358 if line and needs_space: 359 newlines.append(" " + line) 360 else: 361 newlines.append(line) 362 newnotes = "\n".join(newlines) 363 if origin in ["programmer", "developer", "source code"]: 364 gpo.po_message_set_extracted_comments(self._gpo_message, newnotes) 365 else: 366 gpo.po_message_set_comments(self._gpo_message, newnotes)
367
368 - def removenotes(self):
369 gpo.po_message_set_comments(self._gpo_message, "")
370
371 - def copy(self):
372 newpo = self.__class__() 373 newpo._gpo_message = self._gpo_message 374 return newpo
375
376 - def merge(self, otherpo, overwrite=False, comments=True, authoritative=False):
377 """Merges the otherpo (with the same msgid) into this one. 378 379 Overwrite non-blank self.msgstr only if overwrite is True 380 merge comments only if comments is True 381 """ 382 383 if not isinstance(otherpo, pounit): 384 super(pounit, self).merge(otherpo, overwrite, comments) 385 return 386 if comments: 387 self.addnote(otherpo.getnotes("translator"), origin="translator", position="merge") 388 # FIXME mergelists(self.typecomments, otherpo.typecomments) 389 if not authoritative: 390 # We don't bring across otherpo.automaticcomments as we consider ourself 391 # to be the the authority. Same applies to otherpo.msgidcomments 392 self.addnote(otherpo.getnotes("developer"), origin="developer", position="merge") 393 self.msgidcomment = otherpo._extract_msgidcomments() or None 394 self.addlocations(otherpo.getlocations()) 395 if not self.istranslated() or overwrite: 396 # Remove kde-style comments from the translation (if any). 397 if self._extract_msgidcomments(otherpo.target): 398 otherpo.target = otherpo.target.replace('_: ' + otherpo._extract_msgidcomments()+ '\n', '') 399 self.target = otherpo.target 400 if self.source != otherpo.source or self.getcontext() != otherpo.getcontext(): 401 self.markfuzzy() 402 else: 403 self.markfuzzy(otherpo.isfuzzy()) 404 elif not otherpo.istranslated(): 405 if self.source != otherpo.source: 406 self.markfuzzy() 407 else: 408 if self.target != otherpo.target: 409 self.markfuzzy()
410
411 - def isheader(self):
412 #return self.source == u"" and self.target != u"" 413 # we really want to make sure that there is no msgidcomment or msgctxt 414 return self.getid() == "" and len(self.target) > 0
415
416 - def isblank(self):
417 return len(self.source) == len(self.target) == len(self.getcontext()) == 0
418
419 - def hastypecomment(self, typecomment):
420 return gpo.po_message_is_format(self._gpo_message, typecomment)
421
422 - def settypecomment(self, typecomment, present=True):
423 gpo.po_message_set_format(self._gpo_message, typecomment, present)
424
425 - def hasmarkedcomment(self, commentmarker):
426 commentmarker = "(%s)" % commentmarker 427 for comment in self.getnotes("translator").split("\n"): 428 if comment.startswith(commentmarker): 429 return True 430 return False
431
432 - def istranslated(self):
433 return super(pounit, self).istranslated() and not self.isobsolete()
434
435 - def istranslatable(self):
436 return not (self.isheader() or self.isblank() or self.isobsolete())
437
438 - def isfuzzy(self):
439 return gpo.po_message_is_fuzzy(self._gpo_message)
440
441 - def markfuzzy(self, present=True):
442 gpo.po_message_set_fuzzy(self._gpo_message, present)
443
444 - def isreview(self):
445 return self.hasmarkedcomment("review") or self.hasmarkedcomment("pofilter")
446
447 - def isobsolete(self):
448 return gpo.po_message_is_obsolete(self._gpo_message)
449
450 - def makeobsolete(self):
451 # FIXME: libgettexpo currently does not reset other data, we probably want to do that 452 # but a better solution would be for libgettextpo to output correct data on serialisation 453 gpo.po_message_set_obsolete(self._gpo_message, True)
454
455 - def resurrect(self):
456 gpo.po_message_set_obsolete(self._gpo_message, False)
457
458 - def hasplural(self):
459 return gpo.po_message_msgid_plural(self._gpo_message) is not None
460
461 - def _extract_msgidcomments(self, text=None):
462 """Extract KDE style msgid comments from the unit. 463 464 @rtype: String 465 @return: Returns the extracted msgidcomments found in this unit's msgid. 466 467 """ 468 469 if not text: 470 text = gpo.po_message_msgid(self._gpo_message) 471 if text: 472 return pocommon.extract_msgid_comment(text) 473 return u""
474
475 - def setmsgidcomment(self, msgidcomment):
476 if msgidcomment: 477 newsource = "_: " + msgidcomment + "\n" + self.source 478 self.source = newsource
479 msgidcomment = property(_extract_msgidcomments, setmsgidcomment) 480
481 - def __str__(self):
482 pf = pofile() 483 pf.addunit(self) 484 return str(pf)
485
486 - def getlocations(self):
487 locations = [] 488 i = 0 489 location = gpo.po_message_filepos(self._gpo_message, i) 490 while location: 491 locname = gpo.po_filepos_file(location) 492 locline = gpo.po_filepos_start_line(location) 493 if locline == -1: 494 locstring = locname 495 else: 496 locstring = locname + ":" + str(locline) 497 locations.append(locstring) 498 i += 1 499 location = gpo.po_message_filepos(self._gpo_message, i) 500 return locations
501
502 - def addlocation(self, location):
503 for loc in location.split(): 504 parts = loc.split(":") 505 file = parts[0] 506 if len(parts) == 2: 507 line = int(parts[1]) 508 else: 509 line = -1 510 gpo.po_message_add_filepos(self._gpo_message, file, line)
511
512 - def getcontext(self):
513 msgctxt = gpo.po_message_msgctxt(self._gpo_message) 514 if msgctxt: 515 return msgctxt.decode(self._encoding) 516 else: 517 msgidcomment = self._extract_msgidcomments() 518 return msgidcomment
519
520 - def buildfromunit(cls, unit):
521 """Build a native unit from a foreign unit, preserving as much 522 information as possible.""" 523 if type(unit) == cls and hasattr(unit, "copy") and callable(unit.copy): 524 return unit.copy() 525 elif isinstance(unit, pocommon.pounit): 526 newunit = cls(unit.source) 527 newunit.target = unit.target 528 #context 529 newunit.msgidcomment = unit._extract_msgidcomments() 530 context = unit.getcontext() 531 if not newunit.msgidcomment and context: 532 gpo.po_message_set_msgctxt(newunit._gpo_message, context) 533 534 locations = unit.getlocations() 535 if locations: 536 newunit.addlocations(locations) 537 notes = unit.getnotes("developer") 538 if notes: 539 newunit.addnote(notes, "developer") 540 notes = unit.getnotes("translator") 541 if notes: 542 newunit.addnote(notes, "translator") 543 if unit.isobsolete(): 544 newunit.makeobsolete() 545 newunit.markfuzzy(unit.isfuzzy()) 546 for tc in ['python-format', 'c-format', 'php-format']: 547 if unit.hastypecomment(tc): 548 newunit.settypecomment(tc) 549 # We assume/guess/hope that there will only be one 550 break 551 return newunit 552 else: 553 return base.buildfromunit(unit)
554 buildfromunit = classmethod(buildfromunit) 555
556 -class pofile(pocommon.pofile):
557 UnitClass = pounit
558 - def __init__(self, inputfile=None, encoding=None, unitclass=pounit):
559 self.UnitClass = unitclass 560 pocommon.pofile.__init__(self, unitclass=unitclass) 561 self._gpo_memory_file = None 562 self._gpo_message_iterator = None 563 self._encoding = encodingToUse(encoding) 564 if inputfile is not None: 565 self.parse(inputfile) 566 else: 567 self._gpo_memory_file = gpo.po_file_create() 568 self._gpo_message_iterator = gpo.po_message_iterator(self._gpo_memory_file, None)
569
570 - def addunit(self, unit, new=True):
571 if new: 572 gpo.po_message_insert(self._gpo_message_iterator, unit._gpo_message) 573 super(pofile, self).addunit(unit)
574
575 - def removeduplicates(self, duplicatestyle="merge"):
576 """make sure each msgid is unique ; merge comments etc from duplicates into original""" 577 # TODO: can we handle consecutive calls to removeduplicates()? What 578 # about files already containing msgctxt? - test 579 id_dict = {} 580 uniqueunits = [] 581 # TODO: this is using a list as the pos aren't hashable, but this is slow. 582 # probably not used frequently enough to worry about it, though. 583 markedpos = [] 584 def addcomment(thepo): 585 thepo.msgidcomment = " ".join(thepo.getlocations()) 586 markedpos.append(thepo)
587 for thepo in self.units: 588 id = thepo.getid() 589 if thepo.isheader() and not thepo.getlocations(): 590 # header msgids shouldn't be merged... 591 uniqueunits.append(thepo) 592 elif id in id_dict: 593 if duplicatestyle == "merge": 594 if id: 595 id_dict[id].merge(thepo) 596 else: 597 addcomment(thepo) 598 uniqueunits.append(thepo) 599 elif duplicatestyle == "msgctxt": 600 origpo = id_dict[id] 601 if origpo not in markedpos: 602 gpo.po_message_set_msgctxt(origpo._gpo_message, " ".join(origpo.getlocations())) 603 markedpos.append(thepo) 604 gpo.po_message_set_msgctxt(thepo._gpo_message, " ".join(thepo.getlocations())) 605 uniqueunits.append(thepo) 606 else: 607 if not id: 608 if duplicatestyle == "merge": 609 addcomment(thepo) 610 else: 611 gpo.po_message_set_msgctxt(thepo._gpo_message, " ".join(thepo.getlocations())) 612 id_dict[id] = thepo 613 uniqueunits.append(thepo) 614 new_gpo_memory_file = gpo.po_file_create() 615 new_gpo_message_iterator = gpo.po_message_iterator(new_gpo_memory_file, None) 616 for unit in uniqueunits: 617 gpo.po_message_insert(new_gpo_message_iterator, unit._gpo_message) 618 gpo.po_message_iterator_free(self._gpo_message_iterator) 619 self._gpo_message_iterator = new_gpo_message_iterator 620 self._gpo_memory_file = new_gpo_memory_file 621 self.units = uniqueunits
622
623 - def __str__(self):
624 def obsolete_workaround(): 625 # Remove all items that are not output by msgmerge when a unit is obsolete. This is a work 626 # around for bug in libgettextpo 627 # FIXME Do version test in case they fix this bug 628 for unit in self.units: 629 if unit.isobsolete(): 630 gpo.po_message_set_extracted_comments(unit._gpo_message, "") 631 location = gpo.po_message_filepos(unit._gpo_message, 0) 632 while location: 633 gpo.po_message_remove_filepos(unit._gpo_message, 0) 634 location = gpo.po_message_filepos(unit._gpo_message, 0)
635 outputstring = "" 636 if self._gpo_memory_file: 637 obsolete_workaround() 638 f, fname = tempfile.mkstemp(prefix='translate', suffix='.po') 639 os.close(f) 640 self._gpo_memory_file = gpo.po_file_write_v2(self._gpo_memory_file, fname, xerror_handler) 641 f = open(fname) 642 outputstring = f.read() 643 f.close() 644 os.remove(fname) 645 return outputstring 646
647 - def isempty(self):
648 """Returns True if the object doesn't contain any translation units.""" 649 if len(self.units) == 0: 650 return True 651 # Skip the first unit if it is a header. 652 if self.units[0].isheader(): 653 units = self.units[1:] 654 else: 655 units = self.units 656 657 for unit in units: 658 if not unit.isblank() and not unit.isobsolete(): 659 return False 660 return True
661
662 - def parse(self, input):
663 if hasattr(input, 'name'): 664 self.filename = input.name 665 elif not getattr(self, 'filename', ''): 666 self.filename = '' 667 668 if hasattr(input, "read"): 669 posrc = input.read() 670 input.close() 671 input = posrc 672 673 needtmpfile = not os.path.isfile(input) 674 if needtmpfile: 675 # This is not a file - we write the string to a temporary file 676 fd, fname = tempfile.mkstemp(prefix='translate', suffix='.po') 677 os.write(fd, input) 678 input = fname 679 os.close(fd) 680 681 self._gpo_memory_file = gpo.po_file_read_v3(input, xerror_handler) 682 if self._gpo_memory_file is None: 683 print >> sys.stderr, "Error:" 684 685 if needtmpfile: 686 os.remove(input) 687 688 # Handle xerrors here 689 self._header = gpo.po_file_domain_header(self._gpo_memory_file, None) 690 if self._header: 691 charset = gpo.po_header_field(self._header, "Content-Type") 692 if charset: 693 charset = re.search("charset=([^\\s]+)", charset).group(1) 694 self._encoding = encodingToUse(charset) 695 self._gpo_message_iterator = gpo.po_message_iterator(self._gpo_memory_file, None) 696 newmessage = gpo.po_next_message(self._gpo_message_iterator) 697 while newmessage: 698 newunit = pounit(gpo_message=newmessage) 699 self.addunit(newunit, new=False) 700 newmessage = gpo.po_next_message(self._gpo_message_iterator) 701 self._free_iterator()
702
703 - def __del__(self):
704 # We currently disable this while we still get segmentation faults. 705 # Note that this is definitely leaking memory because of this. 706 return 707 self._free_iterator() 708 if self._gpo_memory_file is not None: 709 gpo.po_file_free(self._gpo_memory_file) 710 self._gpo_memory_file = None
711
712 - def _free_iterator(self):
713 # We currently disable this while we still get segmentation faults. 714 # Note that this is definitely leaking memory because of this. 715 return 716 if self._gpo_message_iterator is not None: 717 gpo.po_message_iterator_free(self._gpo_message_iterator) 718 self._gpo_message_iterator = None
719