1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 """XLIFF classes specifically suited for handling the PO representation in
22 XLIFF.
23
24 This way the API supports plurals as if it was a PO file, for example.
25 """
26
27 from lxml import etree
28 import re
29
30 from translate.misc.multistring import multistring
31 from translate.storage import base, lisa, poheader, xliff
32 from translate.storage.placeables import general
33
34
36 if not isinstance(thing, multistring):
37 return False
38 return len(thing.strings) > 1
39
40
42 """A class to specifically handle the plural units created from a po file."""
43
44 rich_parsers = general.parsers
45
46 - def __init__(self, source=None, empty=False, encoding="UTF-8"):
62
64 if isinstance(other, PoXliffUnit):
65 if len(self.units) != len(other.units):
66 return False
67 if not super(PoXliffUnit, self).__eq__(other):
68 return False
69 for i in range(len(self.units)-1):
70 if not self.units[i+1] == other.units[i+1]:
71 return False
72 return True
73 if len(self.units) <= 1:
74 if isinstance(other, lisa.LISAunit):
75 return super(PoXliffUnit, self).__eq__(other)
76 else:
77 return self.source == other.source and self.target == other.target
78 return False
79
80
81
82
83
84
85
86
87 - def setsource(self, source, sourcelang="en"):
106
107
108 multistring_to_rich = base.TranslationUnit.multistring_to_rich
109 rich_to_multistring = base.TranslationUnit.rich_to_multistring
110
111 rich_source = base.TranslationUnit.rich_source
112 rich_target = base.TranslationUnit.rich_target
113
121 source = property(getsource, setsource)
122
123 - def settarget(self, text, lang='xx', append=False):
148
158
159 target = property(gettarget, settarget)
160
161 - def addnote(self, text, origin=None, position="append"):
162 """Add a note specifically in a "note" tag"""
163 if isinstance(text, str):
164 text = text.decode("utf-8")
165 note = etree.SubElement(self.xmlelement, self.namespaced("note"))
166 note.text = text
167 if origin:
168 note.set("from", origin)
169 for unit in self.units[1:]:
170 unit.addnote(text, origin)
171
173
174 if origin == "translator":
175 notes = super(PoXliffUnit, self).getnotes("translator")
176 trancomments = self.gettranslatorcomments()
177 if notes == trancomments or trancomments.find(notes) >= 0:
178 notes = ""
179 elif notes.find(trancomments) >= 0:
180 trancomments = notes
181 notes = ""
182 trancomments = trancomments + notes
183 return trancomments
184 elif origin in ["programmer", "developer", "source code"]:
185 devcomments = super(PoXliffUnit, self).getnotes("developer")
186 autocomments = self.getautomaticcomments()
187 if devcomments == autocomments or autocomments.find(devcomments) >= 0:
188 devcomments = ""
189 elif devcomments.find(autocomments) >= 0:
190 autocomments = devcomments
191 devcomments = ""
192 return autocomments
193 else:
194 return super(PoXliffUnit, self).getnotes(origin)
195
200
205
207 super(PoXliffUnit, self).setid(id)
208 if len(self.units) > 1:
209 for i in range(len(self.units)):
210 self.units[i].setid("%s[%d]" % (id, i))
211
213 """Returns all the references (source locations)"""
214 groups = self.getcontextgroups("po-reference")
215 references = []
216 for group in groups:
217 sourcefile = ""
218 linenumber = ""
219 for (type, text) in group:
220 if type == "sourcefile":
221 sourcefile = text
222 elif type == "linenumber":
223 linenumber = text
224 assert sourcefile
225 if linenumber:
226 sourcefile = sourcefile + ":" + linenumber
227 references.append(sourcefile)
228 return references
229
236 groups = self.getcontextgroups("po-entry")
237 comments = []
238 for group in groups:
239 commentpairs = filter(hasautocomment, group)
240 for (type, text) in commentpairs:
241 comments.append(text)
242 return "\n".join(comments)
243
250 groups = self.getcontextgroups("po-entry")
251 comments = []
252 for group in groups:
253 commentpairs = filter(hastrancomment, group)
254 for (type, text) in commentpairs:
255 comments.append(text)
256 return "\n".join(comments)
257
259 return "gettext-domain-header" in (self.getrestype() or "")
260
263
265 if element.tag.endswith("trans-unit"):
266 object = cls(None, empty=True)
267 object.xmlelement = element
268 object.namespace = namespace
269 return object
270 assert element.tag.endswith("group")
271 group = cls(None, empty=True)
272 group.xmlelement = element
273 group.namespace = namespace
274 units = list(element.iterdescendants(group.namespaced('trans-unit')))
275 for unit in units:
276 subunit = xliff.xliffunit.createfromxmlElement(unit)
277 subunit.namespace = namespace
278 group.units.append(subunit)
279 return group
280 createfromxmlElement = classmethod(createfromxmlElement)
281
283 return self.xmlelement.tag == self.namespaced("group")
284
285
287 """a file for the po variant of Xliff files"""
288 UnitClass = PoXliffUnit
289
291 if not "sourcelanguage" in kwargs:
292 kwargs["sourcelanguage"] = "en-US"
293 xliff.xlifffile.__init__(self, *args, **kwargs)
294
295 - def createfilenode(self, filename, sourcelanguage="en-US", datatype="po"):
299
305
307 unit = self.addsourceunit(target, filename, True)
308 unit.target = target
309 unit.xmlelement.set("restype", "x-gettext-domain-header")
310 unit.xmlelement.set("approved", "no")
311 lisa.setXMLspace(unit.xmlelement, "preserve")
312 return unit
313
314 - def addplural(self, source, target, filename, createifmissing=False):
315 """This method should now be unnecessary, but is left for reference"""
316 assert isinstance(source, multistring)
317 if not isinstance(target, multistring):
318 target = multistring(target)
319 sourcel = len(source.strings)
320 targetl = len(target.strings)
321 if sourcel < targetl:
322 sources = source.strings + [source.strings[-1]] * targetl - sourcel
323 targets = target.strings
324 else:
325 sources = source.strings
326 targets = target.strings
327 self._messagenum += 1
328 pluralnum = 0
329 group = self.creategroup(filename, True, restype="x-gettext-plural")
330 for (src, tgt) in zip(sources, targets):
331 unit = self.UnitClass(src)
332 unit.target = tgt
333 unit.setid("%d[%d]" % (self._messagenum, pluralnum))
334 pluralnum += 1
335 group.append(unit.xmlelement)
336 self.units.append(unit)
337
338 if pluralnum < sourcel:
339 for string in sources[pluralnum:]:
340 unit = self.UnitClass(src)
341 unit.xmlelement.set("translate", "no")
342 unit.setid("%d[%d]" % (self._messagenum, pluralnum))
343 pluralnum += 1
344 group.append(unit.xmlelement)
345 self.units.append(unit)
346
347 return self.units[-pluralnum]
348
350 """Populates this object from the given xml string"""
351
352
353 def ispluralgroup(node):
354 """determines whether the xml node refers to a getttext plural"""
355 return node.get("restype") == "x-gettext-plurals"
356
357 def isnonpluralunit(node):
358 """determindes whether the xml node contains a plural like id.
359
360 We want to filter out all the plural nodes, except the very first
361 one in each group.
362 """
363 return re.match(r"\d+\[[123456]\]$", node.get("id") or "") is None
364
365 def pluralunits(pluralgroups):
366 for pluralgroup in pluralgroups:
367 yield self.UnitClass.createfromxmlElement(pluralgroup, namespace=self.namespace)
368
369 self.filename = getattr(xml, 'name', '')
370 if hasattr(xml, "read"):
371 xml.seek(0)
372 xmlsrc = xml.read()
373 xml = xmlsrc
374 self.document = etree.fromstring(xml).getroottree()
375 self.initbody()
376 root_node = self.document.getroot()
377 assert root_node.tag == self.namespaced(self.rootNode)
378 groups = root_node.iterdescendants(self.namespaced("group"))
379 pluralgroups = filter(ispluralgroup, groups)
380 termEntries = root_node.iterdescendants(self.namespaced(self.UnitClass.rootNode))
381
382 singularunits = filter(isnonpluralunit, termEntries)
383 if len(singularunits) == 0:
384 return
385 pluralunit_iter = pluralunits(pluralgroups)
386 try:
387 nextplural = pluralunit_iter.next()
388 except StopIteration:
389 nextplural = None
390
391 for entry in singularunits:
392 term = self.UnitClass.createfromxmlElement(entry, namespace=self.namespace)
393 if nextplural and unicode(term.getid()) == ("%s[0]" % nextplural.getid()):
394 self.addunit(nextplural, new=False)
395 try:
396 nextplural = pluralunit_iter.next()
397 except StopIteration, i:
398 nextplural = None
399 else:
400 self.addunit(term, new=False)
401