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 translate.storage import base, lisa, poheader, xliff
28 from translate.storage.placeables import general
29 from translate.misc.multistring import multistring
30 from lxml import etree
31 import re
32
34 if not isinstance(thing, multistring):
35 return False
36 return len(thing.strings) > 1
37
39 """A class to specifically handle the plural units created from a po file."""
40
41 rich_parsers = general.parsers
42
43 - def __init__(self, source=None, empty=False, encoding="UTF-8"):
59
61 if isinstance(other, PoXliffUnit):
62 if len(self.units) != len(other.units):
63 return False
64 if not super(PoXliffUnit, self).__eq__(other):
65 return False
66 for i in range(len(self.units)-1):
67 if not self.units[i+1] == other.units[i+1]:
68 return False
69 return True
70 if len(self.units) <= 1:
71 if isinstance(other, lisa.LISAunit):
72 return super(PoXliffUnit, self).__eq__(other)
73 else:
74 return self.source == other.source and self.target == other.target
75 return False
76
77
78
79
80
81
82
83
84 - def setsource(self, source, sourcelang="en"):
85
86 self._rich_source = None
87 if not hasplurals(source):
88 super(PoXliffUnit, self).setsource(source, sourcelang)
89 else:
90 target = self.target
91 for unit in self.units:
92 try:
93 self.xmlelement.remove(unit.xmlelement)
94 except xml.dom.NotFoundErr:
95 pass
96 self.units = []
97 for s in source.strings:
98 newunit = xliff.xliffunit(s)
99
100 self.units.append(newunit)
101 self.xmlelement.append(newunit.xmlelement)
102 self.target = target
103
104
105 multistring_to_rich = base.TranslationUnit.multistring_to_rich
106 rich_to_multistring = base.TranslationUnit.rich_to_multistring
107
108 rich_source = base.TranslationUnit.rich_source
109 rich_target = base.TranslationUnit.rich_target
110
118 source = property(getsource, setsource)
119
120 - def settarget(self, text, lang='xx', append=False):
145
155
156 target = property(gettarget, settarget)
157
158 - def addnote(self, text, origin=None, position="append"):
159 """Add a note specifically in a "note" tag"""
160 if isinstance(text, str):
161 text = text.decode("utf-8")
162 note = etree.SubElement(self.xmlelement, self.namespaced("note"))
163 note.text = text
164 if origin:
165 note.set("from", origin)
166 for unit in self.units[1:]:
167 unit.addnote(text, origin)
168
170
171 if origin == "translator":
172 notes = super(PoXliffUnit, self).getnotes("translator")
173 trancomments = self.gettranslatorcomments()
174 if notes == trancomments or trancomments.find(notes) >= 0:
175 notes = ""
176 elif notes.find(trancomments) >= 0:
177 trancomments = notes
178 notes = ""
179 trancomments = trancomments + notes
180 return trancomments
181 elif origin in ["programmer", "developer", "source code"]:
182 devcomments = super(PoXliffUnit, self).getnotes("developer")
183 autocomments = self.getautomaticcomments()
184 if devcomments == autocomments or autocomments.find(devcomments) >= 0:
185 devcomments = ""
186 elif devcomments.find(autocomments) >= 0:
187 autocomments = devcomments
188 devcomments = ""
189 return autocomments
190 else:
191 return super(PoXliffUnit, self).getnotes(origin)
192
197
202
204 self.xmlelement.set("id", id)
205 if len(self.units) > 1:
206 for i in range(len(self.units)):
207 self.units[i].setid("%s[%d]" % (id, i))
208
210 """Returns all the references (source locations)"""
211 groups = self.getcontextgroups("po-reference")
212 references = []
213 for group in groups:
214 sourcefile = ""
215 linenumber = ""
216 for (type, text) in group:
217 if type == "sourcefile":
218 sourcefile = text
219 elif type == "linenumber":
220 linenumber = text
221 assert sourcefile
222 if linenumber:
223 sourcefile = sourcefile + ":" + linenumber
224 references.append(sourcefile)
225 return references
226
232 groups = self.getcontextgroups("po-entry")
233 comments = []
234 for group in groups:
235 commentpairs = filter(hasautocomment, group)
236 for (type, text) in commentpairs:
237 comments.append(text)
238 return "\n".join(comments)
239
245 groups = self.getcontextgroups("po-entry")
246 comments = []
247 for group in groups:
248 commentpairs = filter(hastrancomment, group)
249 for (type, text) in commentpairs:
250 comments.append(text)
251 return "\n".join(comments)
252
254 return "gettext-domain-header" in (self.getrestype() or "")
255
258
260 if element.tag.endswith("trans-unit"):
261 object = cls(None, empty=True)
262 object.xmlelement = element
263 object.namespace = namespace
264 return object
265 assert element.tag.endswith("group")
266 group = cls(None, empty=True)
267 group.xmlelement = element
268 group.namespace = namespace
269 units = list(element.iterdescendants(group.namespaced('trans-unit')))
270 for unit in units:
271 subunit = xliff.xliffunit.createfromxmlElement(unit)
272 subunit.namespace = namespace
273 group.units.append(subunit)
274 return group
275 createfromxmlElement = classmethod(createfromxmlElement)
276
278 return self.xmlelement.tag == self.namespaced("group")
279
280
282 """a file for the po variant of Xliff files"""
283 UnitClass = PoXliffUnit
285 if not "sourcelanguage" in kwargs:
286 kwargs["sourcelanguage"] = "en-US"
287 xliff.xlifffile.__init__(self, *args, **kwargs)
288
289 - def createfilenode(self, filename, sourcelanguage="en-US", datatype="po"):
293
299
301 unit = self.addsourceunit(target, filename, True)
302 unit.target = target
303 unit.xmlelement.set("restype", "x-gettext-domain-header")
304 unit.xmlelement.set("approved", "no")
305 lisa.setXMLspace(unit.xmlelement, "preserve")
306 return unit
307
308 - def addplural(self, source, target, filename, createifmissing=False):
309 """This method should now be unnecessary, but is left for reference"""
310 assert isinstance(source, multistring)
311 if not isinstance(target, multistring):
312 target = multistring(target)
313 sourcel = len(source.strings)
314 targetl = len(target.strings)
315 if sourcel < targetl:
316 sources = source.strings + [source.strings[-1]] * targetl - sourcel
317 targets = target.strings
318 else:
319 sources = source.strings
320 targets = target.strings
321 self._messagenum += 1
322 pluralnum = 0
323 group = self.creategroup(filename, True, restype="x-gettext-plural")
324 for (src, tgt) in zip(sources, targets):
325 unit = self.UnitClass(src)
326 unit.target = tgt
327 unit.setid("%d[%d]" % (self._messagenum, pluralnum))
328 pluralnum += 1
329 group.append(unit.xmlelement)
330 self.units.append(unit)
331
332 if pluralnum < sourcel:
333 for string in sources[pluralnum:]:
334 unit = self.UnitClass(src)
335 unit.xmlelement.set("translate", "no")
336 unit.setid("%d[%d]" % (self._messagenum, pluralnum))
337 pluralnum += 1
338 group.append(unit.xmlelement)
339 self.units.append(unit)
340
341 return self.units[-pluralnum]
342
344 """Populates this object from the given xml string"""
345
346 def ispluralgroup(node):
347 """determines whether the xml node refers to a getttext plural"""
348 return node.get("restype") == "x-gettext-plurals"
349
350 def isnonpluralunit(node):
351 """determindes whether the xml node contains a plural like id.
352
353 We want to filter out all the plural nodes, except the very first
354 one in each group.
355 """
356 return re.match(r"\d+\[[123456]\]$", node.get("id") or "") is None
357
358 def pluralunits(pluralgroups):
359 for pluralgroup in pluralgroups:
360 yield self.UnitClass.createfromxmlElement(pluralgroup, namespace=self.namespace)
361
362 self.filename = getattr(xml, 'name', '')
363 if hasattr(xml, "read"):
364 xml.seek(0)
365 xmlsrc = xml.read()
366 xml = xmlsrc
367 self.document = etree.fromstring(xml).getroottree()
368 self.initbody()
369 root_node = self.document.getroot()
370 assert root_node.tag == self.namespaced(self.rootNode)
371 groups = root_node.iterdescendants(self.namespaced("group"))
372 pluralgroups = filter(ispluralgroup, groups)
373 termEntries = root_node.iterdescendants(self.namespaced(self.UnitClass.rootNode))
374
375 singularunits = filter(isnonpluralunit, termEntries)
376 if len(singularunits) == 0:
377 return
378 pluralunit_iter = pluralunits(pluralgroups)
379 try:
380 nextplural = pluralunit_iter.next()
381 except StopIteration:
382 nextplural = None
383
384 for entry in singularunits:
385 term = self.UnitClass.createfromxmlElement(entry, namespace=self.namespace)
386 if nextplural and unicode(term.getid()) == ("%s[0]" % nextplural.getid()):
387 self.addunit(nextplural, new=False)
388 try:
389 nextplural = pluralunit_iter.next()
390 except StopIteration, i:
391 nextplural = None
392 else:
393 self.addunit(term, new=False)
394