1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """Module for handling Qt linguist (.ts) files.
24
25 This will eventually replace the older ts.py which only supports the older
26 format. While converters haven't been updated to use this module, we retain
27 both.
28
29 U{TS file format 4.3<http://doc.trolltech.com/4.3/linguist-ts-file-format.html>},
30 U{Example<http://svn.ez.no/svn/ezcomponents/trunk/Translation/docs/linguist-format.txt>},
31 U{Plurals forms<http://www.koders.com/cpp/fidE7B7E83C54B9036EB7FA0F27BC56BCCFC4B9DF34.aspx#L200>}
32
33 U{Specification of the valid variable entries <http://doc.trolltech.com/4.3/qstring.html#arg>},
34 U{2 <http://doc.trolltech.com/4.3/qstring.html#arg-2>}
35 """
36
37 from translate.storage import lisa
38 from translate.misc.multistring import multistring
39 from lxml import etree
40
41
42
43 NPLURALS = {
44 'jp': 1,
45 'en': 2,
46 'fr': 2,
47 'lv': 3,
48 'ga': 3,
49 'cs': 3,
50 'sk': 3,
51 'mk': 3,
52 'lt': 3,
53 'ru': 3,
54 'pl': 3,
55 'ro': 3,
56 'sl': 4,
57 'mt': 4,
58 'cy': 5,
59 'ar': 6,
60 }
61
63 """A single term in the xliff file."""
64
65 rootNode = "message"
66 languageNode = "source"
67 textNode = ""
68 namespace = ''
69
71 """Returns an xml Element setup with given parameters."""
72
73 assert purpose
74 if purpose == "target":
75 purpose = "translation"
76 langset = etree.Element(self.namespaced(purpose))
77
78
79
80 langset.text = text
81 return langset
82
85
88
90 """We override this to get source and target nodes."""
91 def not_none(node):
92 return not node is None
93 return filter(not_none, [self._getsourcenode(), self._gettargetnode()])
94
101 source = property(getsource, lisa.LISAunit.setsource)
102
104
105 if self.gettarget() == text:
106 return
107 targetnode = self._gettargetnode()
108 strings = []
109 if isinstance(text, multistring) and (len(text.strings) > 1):
110 strings = text.strings
111 targetnode.set("numerus", "yes")
112 elif self.hasplural():
113
114
115 strings = [text]
116 for string in strings:
117 numerus = etree.SubElement(targetnode, self.namespaced("numerusform"))
118 numerus.text = string
119 else:
120 targetnode.text = text
121
123 targetnode = self._gettargetnode()
124 if targetnode is None:
125 etree.SubElement(self.xmlelement, self.namespaced("translation"))
126 return None
127 if self.hasplural():
128 numerus_nodes = targetnode.findall(self.namespaced("numerusform"))
129 return multistring([node.text for node in numerus_nodes])
130 else:
131 return targetnode.text or ""
132 target = property(gettarget, settarget)
133
135 return self.xmlelement.get("numerus") == "yes"
136
137 - def addnote(self, text, origin=None):
145
147
148 notenode = self.xmlelement.find(self.namespaced("comment"))
149 comment = ''
150 if not notenode is None:
151 comment = notenode.text
152 return comment
153
155 """Remove all the translator notes."""
156 note = self.xmlelement.find(self.namespaced("comment"))
157 if not note is None:
158 self.xmlelement.remove(note)
159
161 """Returns the type of this translation."""
162 return self._gettargetnode().get("type")
163
172
174 """States whether this unit needs to be reviewed"""
175 return self._gettype() == "unfinished"
176
178 return self._gettype() == "unfinished"
179
185
187
188 context_name = self.xmlelement.getparent().find("name").text
189 if context_name is not None:
190 return context_name + self.source
191 else:
192 return self.source
193
194 - def getcontext(self):
195 return self.xmlelement.getparent().find("name").text
196
198 if isinstance(location, str):
199 text = text.decode("utf-8")
200 location = etree.SubElement(self.xmlelement, self.namespaced("location"))
201 filename, line = location.split(':', 1)
202 location.set("filename", filename)
203 location.set("line", line or "")
204
206 location = self.xmlelement.find(self.namespaced("location"))
207 if location:
208 return [':'.join([location.get("filename"), location.get("line")])]
209 else:
210 return []
211
212 - def merge(self, otherunit, overwrite=False, comments=True):
217
219 return self._gettype() == "obsolete"
220
221
222
223
224
225
227 """Class representing a XLIFF file store."""
228 UnitClass = tsunit
229 Name = "Qt Linguist Translation File"
230 Mimetypes = ["Qt Linguist Translation File"]
231 Extensions = ["ts"]
232 rootNode = "TS"
233
234 bodyNode = "context"
235 XMLskeleton = '''<!DOCTYPE TS>
236 <TS>
237 </TS>
238 '''
239 namespace = ''
240
244
245 - def initbody(self):
246 """Initialises self.body."""
247 self.namespace = self.document.getroot().nsmap.get(None, None)
248 if self._contextname:
249 self.body = self.getcontextnode(self._contextname)
250 else:
251 self.body = self.document.getroot()
252
253 - def createcontext(self, contextname, comment=None):
254 """Creates a context node with an optional comment"""
255 context = etree.SubElement(self.document.getroot(), self.namespaced(self.bodyNode))
256 if comment:
257 comment_node = context.SubElement(context, "comment")
258 comment_node.text = comment
259 return context
260
261 - def getcontextname(self, contextnode):
262 """Returns the name of the given context."""
263 return filenode.find(self.namespaced("name")).text
264
265 - def getcontextnames(self):
266 """Returns all contextnames in this TS file."""
267 contextnodes = self.document.findall(self.namespaced("context"))
268 contextnames = [self.getcontextname(contextnode) for contextnode in contextnodes]
269 contextnames = filter(None, contextnames)
270 if len(contextnames) == 1 and contextnames[0] == '':
271 contextnames = []
272 return contextnames
273
274 - def getcontextnode(self, contextname):
275 """Finds the contextnode with the given name."""
276 contextnodes = self.document.findall(self.namespaced("context"))
277 for contextnode in contextnodes:
278 if self.getcontextname(contextnode) == contextname:
279 return contextnode
280 return None
281
282 - def addunit(self, unit, new=True, contextname=None, createifmissing=False):
283 """adds the given trans-unit to the last used body node if the contextname has changed it uses the slow method instead (will create the nodes required if asked). Returns success"""
284 if self._contextname != contextname:
285 if not self.switchcontext(contextname, createifmissing):
286 return None
287 super(tsfile, self).addunit(unit, new)
288
289
290 return unit
291
292 - def switchcontext(self, contextname, createifmissing=False):
293 """Switch the current context to the one named contextname, optionally
294 creating it if it doesn't exist."""
295 self._context_name = contextname
296 contextnode = self.getcontextnode(contextname)
297 if contextnode is None:
298 if not createifmissing:
299 return False
300 contextnode = self.createcontextnode(contextname)
301 self.document.getroot().append(contextnode)
302
303 self.body = contextnode
304 if self.body is None:
305 return False
306 return True
307
314