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

Source Code for Module translate.storage.poxliff

  1  #!/usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3  # 
  4  # Copyright 2006-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   
 23  """An xliff file specifically suited for handling the po representation of  
 24  xliff. """ 
 25   
 26  from translate.storage import xliff 
 27  from translate.storage import lisa  
 28  from translate.storage import poheader 
 29  from translate.misc.multistring import multistring 
 30  from lxml import etree 
 31  import re 
 32   
33 -def hasplurals(thing):
34 if not isinstance(thing, multistring): 35 return False 36 return len(thing.strings) > 1
37
38 -class PoXliffUnit(xliff.xliffunit):
39 """A class to specifically handle the plural units created from a po file."""
40 - def __init__(self, source, empty=False):
41 self.units = [] 42 43 if empty: 44 return 45 46 if not hasplurals(source): 47 super(PoXliffUnit, self).__init__(source) 48 return 49 50 self.xmlelement = etree.Element(self.namespaced("group")) 51 self.xmlelement.set("restype", "x-gettext-plurals") 52 self.setsource(source)
53
54 - def __eq__(self, other):
55 if isinstance(other, PoXliffUnit): 56 if len(self.units) != len(other.units): 57 return False 58 if len(self.units) == 0: 59 return True 60 if not super(PoXliffUnit, self).__eq__(other): 61 return False 62 for i in range(len(self.units)-1): 63 if not self.units[i+1] == other.units[i+1]: 64 return False 65 return True 66 if len(self.units) <= 1: 67 if isinstance(other, lisa.LISAunit): 68 return super(PoXliffUnit, self).__eq__(other) 69 else: 70 return self.source == other.source and self.target == other.target 71 return False
72
73 - def setsource(self, source, sourcelang="en"):
74 # TODO: consider changing from plural to singular, etc. 75 if not hasplurals(source): 76 super(PoXliffUnit, self).setsource(source, sourcelang) 77 else: 78 target = self.target 79 for unit in self.units: 80 try: 81 self.xmlelement.remove(unit.xmlelement) 82 except xml.dom.NotFoundErr: 83 pass 84 self.units = [] 85 for s in source.strings: 86 newunit = xliff.xliffunit(s) 87 # newunit.namespace = self.namespace #XXX?necessary? 88 self.units.append(newunit) 89 self.xmlelement.append(newunit.xmlelement) 90 self.target = target
91
92 - def getsource(self):
93 strings = [super(PoXliffUnit, self).getsource()] 94 strings.extend([unit.source for unit in self.units[1:]]) 95 return multistring(strings)
96 source = property(getsource, setsource) 97
98 - def settarget(self, text, lang='xx', append=False):
99 if self.gettarget() == text: 100 return 101 if not self.hasplural(): 102 super(PoXliffUnit, self).settarget(text, lang, append) 103 return 104 if not isinstance(text, multistring): 105 text = multistring(text) 106 source = self.source 107 sourcel = len(source.strings) 108 targetl = len(text.strings) 109 if sourcel < targetl: 110 sources = source.strings + [source.strings[-1]] * (targetl - sourcel) 111 targets = text.strings 112 id = self.getid() 113 self.source = multistring(sources) 114 self.setid(id) 115 elif targetl < sourcel: 116 targets = text.strings + [""] * (sourcel - targetl) 117 else: 118 targets = text.strings 119 120 for i in range(len(self.units)): 121 self.units[i].target = targets[i]
122
123 - def gettarget(self):
124 if self.hasplural(): 125 strings = [unit.target for unit in self.units] 126 if strings: 127 return multistring(strings) 128 else: 129 return None 130 else: 131 return super(PoXliffUnit, self).gettarget()
132 133 target = property(gettarget, settarget) 134
135 - def addnote(self, text, origin=None):
136 """Add a note specifically in a "note" tag""" 137 if isinstance(text, str): 138 text = text.decode("utf-8") 139 note = etree.SubElement(self.xmlelement, self.namespaced("note")) 140 note.text = text 141 if origin: 142 note.set("from", origin) 143 for unit in self.units[1:]: 144 unit.addnote(text, origin)
145
146 - def getnotes(self, origin=None):
147 #NOTE: We support both <context> and <note> tags in xliff files for comments 148 if origin == "translator": 149 notes = super(PoXliffUnit, self).getnotes("translator") 150 trancomments = self.gettranslatorcomments() 151 if notes == trancomments or trancomments.find(notes) >= 0: 152 notes = "" 153 elif notes.find(trancomments) >= 0: 154 trancomments = notes 155 notes = "" 156 trancomments = trancomments + notes 157 return trancomments 158 elif origin in ["programmer", "developer", "source code"]: 159 devcomments = super(PoXliffUnit, self).getnotes("developer") 160 autocomments = self.getautomaticcomments() 161 if devcomments == autocomments or autocomments.find(devcomments) >= 0: 162 devcomments = "" 163 elif devcomments.find(autocomments) >= 0: 164 autocomments = devcomments 165 devcomments = "" 166 return autocomments 167 else: 168 return super(PoXliffUnit, self).getnotes(origin)
169
170 - def markfuzzy(self, value=True):
171 super(PoXliffUnit, self).markfuzzy(value) 172 for unit in self.units[1:]: 173 unit.markfuzzy(value)
174
175 - def marktranslated(self):
176 super(PoXliffUnit, self).marktranslated() 177 for unit in self.units[1:]: 178 unit.marktranslated()
179
180 - def setid(self, id):
181 self.xmlelement.set("id", id) 182 if len(self.units) > 1: 183 for i in range(len(self.units)): 184 self.units[i].setid("%s[%d]" % (id, i))
185
186 - def getlocations(self):
187 """Returns all the references (source locations)""" 188 groups = self.getcontextgroups("po-reference") 189 references = [] 190 for group in groups: 191 sourcefile = "" 192 linenumber = "" 193 for (type, text) in group: 194 if type == "sourcefile": 195 sourcefile = text 196 elif type == "linenumber": 197 linenumber = text 198 assert sourcefile 199 if linenumber: 200 sourcefile = sourcefile + ":" + linenumber 201 references.append(sourcefile) 202 return references
203
204 - def getautomaticcomments(self):
205 """Returns the automatic comments (x-po-autocomment), which corresponds 206 to the #. style po comments.""" 207 def hasautocomment((type, text)): 208 return type == "x-po-autocomment"
209 groups = self.getcontextgroups("po-entry") 210 comments = [] 211 for group in groups: 212 commentpairs = filter(hasautocomment, group) 213 for (type, text) in commentpairs: 214 comments.append(text) 215 return "\n".join(comments)
216
217 - def gettranslatorcomments(self):
218 """Returns the translator comments (x-po-trancomment), which corresponds 219 to the # style po comments.""" 220 def hastrancomment((type, text)): 221 return type == "x-po-trancomment" 222 groups = self.getcontextgroups("po-entry") 223 comments = [] 224 for group in groups: 225 commentpairs = filter(hastrancomment, group) 226 for (type, text) in commentpairs: 227 comments.append(text) 228 return "\n".join(comments) 229
230 - def isheader(self):
231 return "gettext-domain-header" in (self.getrestype() or "")
232
233 - def createfromxmlElement(cls, element, namespace=None):
234 if element.tag.endswith("trans-unit"): 235 object = cls(None, empty=True) 236 object.xmlelement = element 237 object.namespace = namespace 238 return object 239 assert element.tag.endswith("group") 240 group = cls(None, empty=True) 241 group.xmlelement = element 242 group.namespace = namespace 243 units = element.findall('.//%s' % group.namespaced('trans-unit')) 244 for unit in units: 245 subunit = xliff.xliffunit.createfromxmlElement(unit) 246 subunit.namespace = namespace 247 group.units.append(subunit) 248 return group
249 createfromxmlElement = classmethod(createfromxmlElement) 250
251 - def hasplural(self):
252 return self.xmlelement.tag == self.namespaced("group")
253 254
255 -class PoXliffFile(xliff.xlifffile, poheader.poheader):
256 """a file for the po variant of Xliff files""" 257 UnitClass = PoXliffUnit
258 - def __init__(self, *args, **kwargs):
259 if not "sourcelanguage" in kwargs: 260 kwargs["sourcelanguage"] = "en-US" 261 xliff.xlifffile.__init__(self, *args, **kwargs)
262
263 - def createfilenode(self, filename, sourcelanguage="en-US", datatype="po"):
264 # Let's ignore the sourcelanguage parameter opting for the internal 265 # one. PO files will probably be one language 266 return super(PoXliffFile, self).createfilenode(filename, sourcelanguage=self.sourcelanguage, datatype="po")
267
268 - def addheaderunit(self, target, filename):
269 unit = self.addsourceunit(target, filename, True) 270 unit.target = target 271 unit.xmlelement.set("restype", "x-gettext-domain-header") 272 unit.xmlelement.set("approved", "no") 273 lisa.setXMLspace(unit.xmlelement, "preserve") 274 return unit
275
276 - def addplural(self, source, target, filename, createifmissing=False):
277 """This method should now be unnecessary, but is left for reference""" 278 assert isinstance(source, multistring) 279 if not isinstance(target, multistring): 280 target = multistring(target) 281 sourcel = len(source.strings) 282 targetl = len(target.strings) 283 if sourcel < targetl: 284 sources = source.strings + [source.strings[-1]] * targetl - sourcel 285 targets = target.strings 286 else: 287 sources = source.strings 288 targets = target.strings 289 self._messagenum += 1 290 pluralnum = 0 291 group = self.creategroup(filename, True, restype="x-gettext-plural") 292 for (src, tgt) in zip(sources, targets): 293 unit = self.UnitClass(src) 294 unit.target = tgt 295 unit.setid("%d[%d]" % (self._messagenum, pluralnum)) 296 pluralnum += 1 297 group.append(unit.xmlelement) 298 self.units.append(unit) 299 300 if pluralnum < sourcel: 301 for string in sources[pluralnum:]: 302 unit = self.UnitClass(src) 303 unit.xmlelement.set("translate", "no") 304 unit.setid("%d[%d]" % (self._messagenum, pluralnum)) 305 pluralnum += 1 306 group.append(unit.xmlelement) 307 self.units.append(unit) 308 309 return self.units[-pluralnum]
310
311 - def parse(self, xml):
312 """Populates this object from the given xml string""" 313 #TODO: Make more robust 314 def ispluralgroup(node): 315 """determines whether the xml node refers to a getttext plural""" 316 return node.get("restype") == "x-gettext-plurals"
317 318 def isnonpluralunit(node): 319 """determindes whether the xml node contains a plural like id. 320 321 We want to filter out all the plural nodes, except the very first 322 one in each group. 323 """ 324 return re.match(r"\d+\[[123456]\]$", node.get("id") or "") is None
325 326 def pluralunits(pluralgroups): 327 for pluralgroup in pluralgroups: 328 yield self.UnitClass.createfromxmlElement(pluralgroup, namespace=self.namespace) 329 330 self.filename = getattr(xml, 'name', '') 331 if hasattr(xml, "read"): 332 xml.seek(0) 333 xmlsrc = xml.read() 334 xml = xmlsrc 335 self.document = etree.fromstring(xml).getroottree() 336 self.initbody() 337 assert self.document.getroot().tag == self.namespaced(self.rootNode) 338 groups = self.document.findall(".//%s" % self.namespaced("group")) 339 pluralgroups = filter(ispluralgroup, groups) 340 termEntries = self.body.findall('.//%s' % self.namespaced(self.UnitClass.rootNode)) 341 if termEntries is None: 342 return 343 344 singularunits = filter(isnonpluralunit, termEntries) 345 pluralunit_iter = pluralunits(pluralgroups) 346 try: 347 nextplural = pluralunit_iter.next() 348 except StopIteration: 349 nextplural = None 350 351 for entry in singularunits: 352 term = self.UnitClass.createfromxmlElement(entry, namespace=self.namespace) 353 if nextplural and unicode(term.source) in nextplural.source.strings: 354 self.units.append(nextplural) 355 try: 356 nextplural = pluralunit_iter.next() 357 except StopIteration, i: 358 nextplural = None 359 else: 360 self.units.append(term) 361