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

Source Code for Module translate.storage.xliff

  1  #!/usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3  # 
  4  # Copyright 2005-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  """Module for handling XLIFF files for translation. 
 22   
 23  The official recommendation is to use the extention .xlf for XLIFF files. 
 24  """ 
 25   
 26  from lxml import etree 
 27   
 28  from translate.misc.multistring import multistring 
 29  from translate.storage import base, lisa 
 30  from translate.storage.lisa import getXMLspace 
 31  from translate.storage.placeables.lisa import xml_to_strelem, strelem_to_xml 
 32   
 33  # TODO: handle translation types 
 34   
 35  ID_SEPARATOR = u"\04" 
 36  # ID_SEPARATOR is commonly used through toolkit to generate compound 
 37  # unit ids (for instance to concatenate msgctxt and msgid in po), but 
 38  # \04 is an illegal char in XML 1.0, ID_SEPARATOR_SAFE will be used 
 39  # instead when converting between xliff and other toolkit supported 
 40  # formats 
 41  ID_SEPARATOR_SAFE = u"__%04__" 
42 -class xliffunit(lisa.LISAunit):
43 """A single term in the xliff file.""" 44 45 rootNode = "trans-unit" 46 languageNode = "source" 47 textNode = "" 48 namespace = 'urn:oasis:names:tc:xliff:document:1.1' 49 50 _default_xml_space = "default" 51 52 #TODO: id and all the trans-unit level stuff 53
54 - def __init__(self, source, empty=False, **kwargs):
55 """Override the constructor to set xml:space="preserve".""" 56 super(xliffunit, self).__init__(source, empty, **kwargs) 57 if empty: 58 return 59 lisa.setXMLspace(self.xmlelement, "preserve")
60
61 - def createlanguageNode(self, lang, text, purpose):
62 """Returns an xml Element setup with given parameters.""" 63 64 #TODO: for now we do source, but we have to test if it is target, perhaps 65 # with parameter. Alternatively, we can use lang, if supplied, since an xliff 66 #file has to conform to the bilingual nature promised by the header. 67 assert purpose 68 langset = etree.Element(self.namespaced(purpose)) 69 #TODO: check language 70 # lisa.setXMLlang(langset, lang) 71 72 # self.createPHnodes(langset, text) 73 langset.text = text 74 return langset
75
76 - def getlanguageNodes(self):
77 """We override this to get source and target nodes.""" 78 source = None 79 target = None 80 nodes = [] 81 try: 82 source = self.xmlelement.iterchildren(self.namespaced(self.languageNode)).next() 83 target = self.xmlelement.iterchildren(self.namespaced('target')).next() 84 nodes = [source, target] 85 except StopIteration: 86 if source is not None: 87 nodes.append(source) 88 if not target is None: 89 nodes.append(target) 90 return nodes
91
92 - def set_rich_source(self, value, sourcelang='en'):
93 sourcelanguageNode = self.get_source_dom() 94 if sourcelanguageNode is None: 95 sourcelanguageNode = self.createlanguageNode(sourcelang, u'', "source") 96 self.set_source_dom(sourcelanguageNode) 97 98 # Clear sourcelanguageNode first 99 for i in range(len(sourcelanguageNode)): 100 del sourcelanguageNode[0] 101 sourcelanguageNode.text = None 102 103 strelem_to_xml(sourcelanguageNode, value[0])
104
105 - def get_rich_source(self):
106 #rsrc = xml_to_strelem(self.source_dom) 107 #logging.debug('rich source: %s' % (repr(rsrc))) 108 #from dubulib.debug.misc import print_stack_funcs 109 #print_stack_funcs() 110 return [xml_to_strelem(self.source_dom, getXMLspace(self.xmlelement, self._default_xml_space))]
111 rich_source = property(get_rich_source, set_rich_source) 112
113 - def set_rich_target(self, value, lang='xx', append=False):
114 if value is None: 115 self.set_target_dom(self.createlanguageNode(lang, u'', "target")) 116 return 117 118 languageNode = self.get_target_dom() 119 if languageNode is None: 120 languageNode = self.createlanguageNode(lang, u'', "target") 121 self.set_target_dom(languageNode, append) 122 123 # Clear languageNode first 124 for i in range(len(languageNode)): 125 del languageNode[0] 126 languageNode.text = None 127 128 strelem_to_xml(languageNode, value[0])
129
130 - def get_rich_target(self, lang=None):
131 """retrieves the "target" text (second entry), or the entry in the 132 specified language, if it exists""" 133 return [xml_to_strelem(self.get_target_dom(lang), getXMLspace(self.xmlelement, self._default_xml_space))]
134 rich_target = property(get_rich_target, set_rich_target) 135
136 - def addalttrans(self, txt, origin=None, lang=None, sourcetxt=None, matchquality=None):
137 """Adds an alt-trans tag and alt-trans components to the unit. 138 139 @type txt: String 140 @param txt: Alternative translation of the source text. 141 """ 142 143 #TODO: support adding a source tag ad match quality attribute. At 144 # the source tag is needed to inject fuzzy matches from a TM. 145 if isinstance(txt, str): 146 txt = txt.decode("utf-8") 147 alttrans = etree.SubElement(self.xmlelement, self.namespaced("alt-trans")) 148 lisa.setXMLspace(alttrans, "preserve") 149 if sourcetxt: 150 if isinstance(sourcetxt, str): 151 sourcetxt = sourcetxt.decode("utf-8") 152 altsource = etree.SubElement(alttrans, self.namespaced("source")) 153 altsource.text = sourcetxt 154 alttarget = etree.SubElement(alttrans, self.namespaced("target")) 155 alttarget.text = txt 156 if matchquality: 157 alttrans.set("match-quality", matchquality) 158 if origin: 159 alttrans.set("origin", origin) 160 if lang: 161 lisa.setXMLlang(alttrans, lang)
162
163 - def getalttrans(self, origin=None):
164 """Returns <alt-trans> for the given origin as a list of units. No 165 origin means all alternatives.""" 166 translist = [] 167 for node in self.xmlelement.iterdescendants(self.namespaced("alt-trans")): 168 if self.correctorigin(node, origin): 169 # We build some mini units that keep the xmlelement. This 170 # makes it easier to delete it if it is passed back to us. 171 newunit = base.TranslationUnit(self.source) 172 173 # the source tag is optional 174 sourcenode = node.iterdescendants(self.namespaced("source")) 175 try: 176 newunit.source = lisa.getText(sourcenode.next(), getXMLspace(node, self._default_xml_space)) 177 except StopIteration: 178 pass 179 180 # must have one or more targets 181 targetnode = node.iterdescendants(self.namespaced("target")) 182 newunit.target = lisa.getText(targetnode.next(), getXMLspace(node, self._default_xml_space)) 183 #TODO: support multiple targets better 184 #TODO: support notes in alt-trans 185 newunit.xmlelement = node 186 187 translist.append(newunit) 188 return translist
189
190 - def delalttrans(self, alternative):
191 """Removes the supplied alternative from the list of alt-trans tags""" 192 self.xmlelement.remove(alternative.xmlelement)
193
194 - def addnote(self, text, origin=None, position="append"):
195 """Add a note specifically in a "note" tag""" 196 if position != "append": 197 self.removenotes(origin=origin) 198 199 if text: 200 text = text.strip() 201 if not text: 202 return 203 if isinstance(text, str): 204 text = text.decode("utf-8") 205 note = etree.SubElement(self.xmlelement, self.namespaced("note")) 206 note.text = text 207 if origin: 208 note.set("from", origin)
209
210 - def getnotelist(self, origin=None):
211 """Private method that returns the text from notes matching 'origin' or all notes.""" 212 notenodes = self.xmlelement.iterdescendants(self.namespaced("note")) 213 # TODO: consider using xpath to construct initial_list directly 214 # or to simply get the correct text from the outset (just remember to 215 # check for duplication. 216 initial_list = [lisa.getText(note, getXMLspace(self.xmlelement, self._default_xml_space)) for note in notenodes if self.correctorigin(note, origin)] 217 218 # Remove duplicate entries from list: 219 dictset = {} 220 notelist = [dictset.setdefault(note, note) for note in initial_list if note not in dictset] 221 222 return notelist
223
224 - def getnotes(self, origin=None):
225 return '\n'.join(self.getnotelist(origin=origin))
226
227 - def removenotes(self, origin="translator"):
228 """Remove all the translator notes.""" 229 notes = self.xmlelement.iterdescendants(self.namespaced("note")) 230 for note in notes: 231 if self.correctorigin(note, origin=origin): 232 self.xmlelement.remove(note)
233
234 - def adderror(self, errorname, errortext):
235 """Adds an error message to this unit.""" 236 #TODO: consider factoring out: some duplication between XLIFF and TMX 237 text = errorname 238 if errortext: 239 text += ': ' + errortext 240 self.addnote(text, origin="pofilter")
241
242 - def geterrors(self):
243 """Get all error messages.""" 244 #TODO: consider factoring out: some duplication between XLIFF and TMX 245 notelist = self.getnotelist(origin="pofilter") 246 errordict = {} 247 for note in notelist: 248 errorname, errortext = note.split(': ') 249 errordict[errorname] = errortext 250 return errordict
251
252 - def isapproved(self):
253 """States whether this unit is approved.""" 254 return self.xmlelement.get("approved") == "yes"
255
256 - def markapproved(self, value=True):
257 """Mark this unit as approved.""" 258 if value: 259 self.xmlelement.set("approved", "yes") 260 elif self.isapproved(): 261 self.xmlelement.set("approved", "no")
262
263 - def isreview(self):
264 """States whether this unit needs to be reviewed""" 265 targetnode = self.getlanguageNode(lang=None, index=1) 266 return not targetnode is None and \ 267 "needs-review" in targetnode.get("state", "")
268
269 - def markreviewneeded(self, needsreview=True, explanation=None):
270 """Marks the unit to indicate whether it needs review. Adds an optional explanation as a note.""" 271 targetnode = self.getlanguageNode(lang=None, index=1) 272 if not targetnode is None: 273 if needsreview: 274 targetnode.set("state", "needs-review-translation") 275 if explanation: 276 self.addnote(explanation, origin="translator") 277 else: 278 del targetnode.attrib["state"]
279
280 - def isfuzzy(self):
281 # targetnode = self.getlanguageNode(lang=None, index=1) 282 # return not targetnode is None and \ 283 # (targetnode.get("state-qualifier") == "fuzzy-match" or \ 284 # targetnode.get("state") == "needs-review-translation") 285 return not self.isapproved()
286
287 - def markfuzzy(self, value=True):
288 if value: 289 self.markapproved(False) 290 else: 291 self.markapproved(True) 292 targetnode = self.getlanguageNode(lang=None, index=1) 293 if not targetnode is None: 294 if value: 295 targetnode.set("state", "needs-review-translation") 296 else: 297 for attribute in ["state", "state-qualifier"]: 298 if attribute in targetnode.attrib: 299 del targetnode.attrib[attribute]
300
301 - def settarget(self, text, lang='xx', append=False):
302 """Sets the target string to the given value.""" 303 super(xliffunit, self).settarget(text, lang, append) 304 if text: 305 self.marktranslated()
306 307 # This code is commented while this will almost always return false. 308 # This way pocount, etc. works well. 309 # def istranslated(self): 310 # targetnode = self.getlanguageNode(lang=None, index=1) 311 # return not targetnode is None and \ 312 # (targetnode.get("state") == "translated") 313
314 - def istranslatable(self):
315 value = self.xmlelement.get("translate") 316 if value and value.lower() == 'no': 317 return False 318 return True
319
320 - def marktranslated(self):
321 targetnode = self.getlanguageNode(lang=None, index=1) 322 if targetnode is None: 323 return 324 if self.isfuzzy() and "state-qualifier" in targetnode.attrib: 325 #TODO: consider 326 del targetnode.attrib["state-qualifier"] 327 targetnode.set("state", "translated")
328
329 - def setid(self, id):
330 # sanitize id in case ID_SEPERATOR is present 331 self.xmlelement.set("id", id.replace(ID_SEPARATOR, ID_SEPARATOR_SAFE))
332
333 - def getid(self):
334 uid = "" 335 try: 336 filename = self.xmlelement.iterancestors(self.namespaced('file')).next().get('original') 337 if filename: 338 uid = filename + ID_SEPARATOR 339 except StopIteration: 340 # unit has no proper file ancestor, probably newly created 341 pass 342 # hide the fact that we sanitize ID_SEPERATOR 343 uid += (self.xmlelement.get("id") or "").replace(ID_SEPARATOR_SAFE, ID_SEPARATOR) 344 return uid
345
346 - def addlocation(self, location):
347 self.setid(location)
348
349 - def getlocations(self):
350 id_attr = self.xmlelement.get("id") 351 if id_attr: 352 return [id_attr] 353 return []
354
355 - def createcontextgroup(self, name, contexts=None, purpose=None):
356 """Add the context group to the trans-unit with contexts a list with 357 (type, text) tuples describing each context.""" 358 assert contexts 359 group = etree.Element(self.namespaced("context-group")) 360 # context-group tags must appear at the start within <group> 361 # tags. Otherwise it must be appended to the end of a group 362 # of tags. 363 if self.xmlelement.tag == self.namespaced("group"): 364 self.xmlelement.insert(0, group) 365 else: 366 self.xmlelement.append(group) 367 group.set("name", name) 368 if purpose: 369 group.set("purpose", purpose) 370 for type, text in contexts: 371 if isinstance(text, str): 372 text = text.decode("utf-8") 373 context = etree.SubElement(group, self.namespaced("context")) 374 context.text = text 375 context.set("context-type", type)
376
377 - def getcontextgroups(self, name):
378 """Returns the contexts in the context groups with the specified name""" 379 groups = [] 380 grouptags = self.xmlelement.iterdescendants(self.namespaced("context-group")) 381 #TODO: conbine name in query 382 for group in grouptags: 383 if group.get("name") == name: 384 contexts = group.iterdescendants(self.namespaced("context")) 385 pairs = [] 386 for context in contexts: 387 pairs.append((context.get("context-type"), lisa.getText(context, getXMLspace(self.xmlelement, self._default_xml_space)))) 388 groups.append(pairs) #not extend 389 return groups
390
391 - def getrestype(self):
392 """returns the restype attribute in the trans-unit tag""" 393 return self.xmlelement.get("restype")
394
395 - def merge(self, otherunit, overwrite=False, comments=True, authoritative=False):
396 #TODO: consider other attributes like "approved" 397 super(xliffunit, self).merge(otherunit, overwrite, comments) 398 if self.target: 399 self.marktranslated() 400 if otherunit.isfuzzy(): 401 self.markfuzzy() 402 elif otherunit.source == self.source: 403 self.markfuzzy(False) 404 if comments: 405 self.addnote(otherunit.getnotes())
406
407 - def correctorigin(self, node, origin):
408 """Check against node tag's origin (e.g note or alt-trans)""" 409 if origin == None: 410 return True 411 elif origin in node.get("from", ""): 412 return True 413 elif origin in node.get("origin", ""): 414 return True 415 else: 416 return False
417
418 - def multistring_to_rich(cls, mstr):
419 """Override L{TranslationUnit.multistring_to_rich} which is used by the 420 C{rich_source} and C{rich_target} properties.""" 421 strings = mstr 422 if isinstance(mstr, multistring): 423 strings = mstr.strings 424 elif isinstance(mstr, basestring): 425 strings = [mstr] 426 427 return [xml_to_strelem(s) for s in strings]
428 multistring_to_rich = classmethod(multistring_to_rich) 429
430 - def rich_to_multistring(cls, elem_list):
431 """Override L{TranslationUnit.rich_to_multistring} which is used by the 432 C{rich_source} and C{rich_target} properties.""" 433 return multistring([unicode(elem) for elem in elem_list])
434 rich_to_multistring = classmethod(rich_to_multistring)
435 436
437 -class xlifffile(lisa.LISAfile):
438 """Class representing a XLIFF file store.""" 439 UnitClass = xliffunit 440 Name = _("XLIFF Translation File") 441 Mimetypes = ["application/x-xliff", "application/x-xliff+xml"] 442 Extensions = ["xlf", "xliff"] 443 rootNode = "xliff" 444 bodyNode = "body" 445 XMLskeleton = '''<?xml version="1.0" ?> 446 <xliff version='1.1' xmlns='urn:oasis:names:tc:xliff:document:1.1'> 447 <file original='NoName' source-language='en' datatype='plaintext'> 448 <body> 449 </body> 450 </file> 451 </xliff>''' 452 namespace = 'urn:oasis:names:tc:xliff:document:1.1' 453 suggestions_in_format = True 454 """xliff units have alttrans tags which can be used to store suggestions""" 455
456 - def __init__(self, *args, **kwargs):
457 self._filename = None 458 lisa.LISAfile.__init__(self, *args, **kwargs) 459 self._messagenum = 0
460
461 - def initbody(self):
462 self.namespace = self.document.getroot().nsmap.get(None, None) 463 464 if self._filename: 465 filenode = self.getfilenode(self._filename, createifmissing=True) 466 else: 467 filenode = self.document.getroot().iterchildren(self.namespaced('file')).next() 468 self.body = self.getbodynode(filenode, createifmissing=True)
469
470 - def addheader(self):
471 """Initialise the file header.""" 472 pass
473
474 - def createfilenode(self, filename, sourcelanguage=None, targetlanguage=None, datatype='plaintext'):
475 """creates a filenode with the given filename. All parameters 476 are needed for XLIFF compliance.""" 477 if sourcelanguage is None: 478 sourcelanguage = self.sourcelanguage 479 if targetlanguage is None: 480 targetlanguage = self.targetlanguage 481 482 # find the default NoName file tag and use it instead of creating a new one 483 for filenode in self.document.getroot().iterchildren(self.namespaced("file")): 484 if filenode.get("original") == "NoName": 485 filenode.set("original", filename) 486 filenode.set("source-language", sourcelanguage) 487 if targetlanguage: 488 filenode.set("target-language", targetlanguage) 489 return filenode 490 491 filenode = etree.Element(self.namespaced("file")) 492 filenode.set("original", filename) 493 filenode.set("source-language", sourcelanguage) 494 if targetlanguage: 495 filenode.set("target-language", targetlanguage) 496 filenode.set("datatype", datatype) 497 bodyNode = etree.SubElement(filenode, self.namespaced(self.bodyNode)) 498 return filenode
499
500 - def getfilename(self, filenode):
501 """returns the name of the given file""" 502 return filenode.get("original")
503
504 - def setfilename(self, filenode, filename):
505 """set the name of the given file""" 506 return filenode.set("original", filename)
507
508 - def getfilenames(self):
509 """returns all filenames in this XLIFF file""" 510 filenodes = self.document.getroot().iterchildren(self.namespaced("file")) 511 filenames = [self.getfilename(filenode) for filenode in filenodes] 512 filenames = filter(None, filenames) 513 if len(filenames) == 1 and filenames[0] == '': 514 filenames = [] 515 return filenames
516
517 - def getfilenode(self, filename, createifmissing=False):
518 """finds the filenode with the given name""" 519 filenodes = self.document.getroot().iterchildren(self.namespaced("file")) 520 for filenode in filenodes: 521 if self.getfilename(filenode) == filename: 522 return filenode 523 if createifmissing: 524 filenode = self.createfilenode(filename) 525 return filenode 526 return None
527
528 - def getids(self, filename=None):
529 if not filename: 530 return super(xlifffile, self).getids() 531 532 self.id_index = {} 533 prefix = filename + ID_SEPARATOR 534 units = (unit for unit in self.units if unit.getid().startswith(prefix)) 535 for index, unit in enumerate(units): 536 self.id_index[unit.getid()[len(prefix):]] = unit 537 return self.id_index.keys()
538
539 - def setsourcelanguage(self, language):
540 if not language: 541 return 542 filenode = self.document.getroot().iterchildren(self.namespaced('file')).next() 543 filenode.set("source-language", language)
544
545 - def getsourcelanguage(self):
546 filenode = self.document.getroot().iterchildren(self.namespaced('file')).next() 547 return filenode.get("source-language")
548 sourcelanguage = property(getsourcelanguage, setsourcelanguage) 549
550 - def settargetlanguage(self, language):
551 if not language: 552 return 553 filenode = self.document.getroot().iterchildren(self.namespaced('file')).next() 554 filenode.set("target-language", language)
555
556 - def gettargetlanguage(self):
557 filenode = self.document.getroot().iterchildren(self.namespaced('file')).next() 558 return filenode.get("target-language")
559 targetlanguage = property(gettargetlanguage, settargetlanguage) 560
561 - def getdatatype(self, filename=None):
562 """Returns the datatype of the stored file. If no filename is given, 563 the datatype of the first file is given.""" 564 if filename: 565 node = self.getfilenode(filename) 566 if not node is None: 567 return node.get("datatype") 568 else: 569 filenames = self.getfilenames() 570 if len(filenames) > 0 and filenames[0] != "NoName": 571 return self.getdatatype(filenames[0]) 572 return ""
573
574 - def getdate(self, filename=None):
575 """Returns the date attribute for the file. If no filename is given, 576 the date of the first file is given. If the date attribute is not 577 specified, None is returned.""" 578 if filename: 579 node = self.getfilenode(filename) 580 if not node is None: 581 return node.get("date") 582 else: 583 filenames = self.getfilenames() 584 if len(filenames) > 0 and filenames[0] != "NoName": 585 return self.getdate(filenames[0]) 586 return None
587
588 - def removedefaultfile(self):
589 """We want to remove the default file-tag as soon as possible if we 590 know if still present and empty.""" 591 filenodes = list(self.document.getroot().iterchildren(self.namespaced("file"))) 592 if len(filenodes) > 1: 593 for filenode in filenodes: 594 if filenode.get("original") == "NoName" and \ 595 not list(filenode.iterdescendants(self.namespaced(self.UnitClass.rootNode))): 596 self.document.getroot().remove(filenode) 597 break
598
599 - def getheadernode(self, filenode, createifmissing=False):
600 """finds the header node for the given filenode""" 601 # TODO: Deprecated? 602 headernode = filenode.iterchildren(self.namespaced("header")) 603 try: 604 return headernode.next() 605 except StopIteration: 606 pass 607 if not createifmissing: 608 return None 609 headernode = etree.SubElement(filenode, self.namespaced("header")) 610 return headernode
611
612 - def getbodynode(self, filenode, createifmissing=False):
613 """finds the body node for the given filenode""" 614 bodynode = filenode.iterchildren(self.namespaced("body")) 615 try: 616 return bodynode.next() 617 except StopIteration: 618 pass 619 if not createifmissing: 620 return None 621 bodynode = etree.SubElement(filenode, self.namespaced("body")) 622 return bodynode
623
624 - def addsourceunit(self, source, filename="NoName", createifmissing=False):
625 """adds the given trans-unit to the last used body node if the 626 filename has changed it uses the slow method instead (will 627 create the nodes required if asked). Returns success""" 628 if self._filename != filename: 629 if not self.switchfile(filename, createifmissing): 630 return None 631 unit = super(xlifffile, self).addsourceunit(source) 632 self._messagenum += 1 633 unit.setid("%d" % self._messagenum) 634 return unit
635
636 - def switchfile(self, filename, createifmissing=False):
637 """adds the given trans-unit (will create the nodes required if asked). Returns success""" 638 self._filename = filename 639 filenode = self.getfilenode(filename) 640 if filenode is None: 641 if not createifmissing: 642 return False 643 filenode = self.createfilenode(filename) 644 self.document.getroot().append(filenode) 645 646 self.body = self.getbodynode(filenode, createifmissing=createifmissing) 647 if self.body is None: 648 return False 649 self._messagenum = len(list(self.body.iterdescendants(self.namespaced("trans-unit")))) 650 #TODO: was 0 based before - consider 651 # messagenum = len(self.units) 652 #TODO: we want to number them consecutively inside a body/file tag 653 #instead of globally in the whole XLIFF file, but using len(self.units) 654 #will be much faster 655 return True
656
657 - def creategroup(self, filename="NoName", createifmissing=False, restype=None):
658 """adds a group tag into the specified file""" 659 if self._filename != filename: 660 if not self.switchfile(filename, createifmissing): 661 return None 662 group = etree.SubElement(self.body, self.namespaced("group")) 663 if restype: 664 group.set("restype", restype) 665 return group
666
667 - def __str__(self):
668 self.removedefaultfile() 669 return super(xlifffile, self).__str__()
670
671 - def parsestring(cls, storestring):
672 """Parses the string to return the correct file object""" 673 xliff = super(xlifffile, cls).parsestring(storestring) 674 if xliff.units: 675 header = xliff.units[0] 676 if ("gettext-domain-header" in (header.getrestype() or "") \ 677 or xliff.getdatatype() == "po") \ 678 and cls.__name__.lower() != "poxlifffile": 679 from translate.storage import poxliff 680 xliff = poxliff.PoXliffFile.parsestring(storestring) 681 return xliff
682 parsestring = classmethod(parsestring)
683