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