1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 """Module for handling Qt linguist (.ts) files.
22
23 This will eventually replace the older ts.py which only supports the older
24 format. While converters haven't been updated to use this module, we retain
25 both.
26
27 U{TS file format 4.3<http://doc.trolltech.com/4.3/linguist-ts-file-format.html>},
28 U{http://doc.trolltech.com/4.5/linguist-ts-file-format.html>},
29 U{Example<http://svn.ez.no/svn/ezcomponents/trunk/Translation/docs/linguist-format.txt>},
30 U{Plurals forms<http://www.koders.com/cpp/fidE7B7E83C54B9036EB7FA0F27BC56BCCFC4B9DF34.aspx#L200>}
31
32 U{Specification of the valid variable entries <http://doc.trolltech.com/4.3/qstring.html#arg>},
33 U{2 <http://doc.trolltech.com/4.3/qstring.html#arg-2>}
34 """
35
36 from translate.storage import base, lisa
37 from translate.storage.placeables import general, StringElem
38 from translate.misc.multistring import multistring
39 from translate.lang import data
40 from lxml import etree
41
42
43
44 NPLURALS = {
45 'jp': 1,
46 'en': 2,
47 'fr': 2,
48 'lv': 3,
49 'ga': 3,
50 'cs': 3,
51 'sk': 3,
52 'mk': 3,
53 'lt': 3,
54 'ru': 3,
55 'pl': 3,
56 'ro': 3,
57 'sl': 4,
58 'mt': 4,
59 'cy': 5,
60 'ar': 6,
61 }
62
96
104 source = property(getsource, lisa.LISAunit.setsource)
105 rich_source = property(base.TranslationUnit._get_rich_source, base.TranslationUnit._set_rich_source)
106
108
109
110
111
112 if self.gettarget() == text:
113 return
114 strings = []
115 if isinstance(text, multistring):
116 strings = text.strings
117 elif isinstance(text, list):
118 strings = text
119 else:
120 strings = [text]
121 targetnode = self._gettargetnode()
122 type = targetnode.get("type")
123 targetnode.clear()
124 if type:
125 targetnode.set("type", type)
126 if self.hasplural() or len(strings) > 1:
127 self.xmlelement.set("numerus", "yes")
128 for string in strings:
129 numerus = etree.SubElement(targetnode, self.namespaced("numerusform"))
130 numerus.text = data.forceunicode(string) or u""
131 else:
132 targetnode.text = data.forceunicode(text) or u""
133
135 targetnode = self._gettargetnode()
136 if targetnode is None:
137 etree.SubElement(self.xmlelement, self.namespaced("translation"))
138 return None
139 if self.hasplural():
140 numerus_nodes = targetnode.findall(self.namespaced("numerusform"))
141 return multistring([node.text or u"" for node in numerus_nodes])
142 else:
143 return data.forceunicode(targetnode.text) or u""
144 target = property(gettarget, settarget)
145 rich_target = property(base.TranslationUnit._get_rich_target, base.TranslationUnit._set_rich_target)
146
148 return self.xmlelement.get("numerus") == "yes"
149
150 - def addnote(self, text, origin=None):
158
160
161 notenode = self.xmlelement.find(self.namespaced("comment"))
162 comment = ''
163 if not notenode is None:
164 comment = notenode.text
165 return comment
166
168 """Remove all the translator notes."""
169 note = self.xmlelement.find(self.namespaced("comment"))
170 if not note is None:
171 self.xmlelement.remove(note)
172
174 """Returns the type of this translation."""
175 targetnode = self._gettargetnode()
176 if targetnode is not None:
177 return targetnode.get("type")
178 return None
179
188
190 """States whether this unit needs to be reviewed"""
191 return self._gettype() == "unfinished"
192
194 return self._gettype() == "unfinished"
195
201
203 context_name = self.getcontext()
204
205
206 if context_name is not None:
207 return context_name + self.source
208 else:
209 return self.source
210
211 - def getcontext(self):
212 return self.xmlelement.getparent().find("name").text
213
215 if isinstance(location, str):
216 text = text.decode("utf-8")
217 location = etree.SubElement(self.xmlelement, self.namespaced("location"))
218 filename, line = location.split(':', 1)
219 location.set("filename", filename)
220 location.set("line", line or "")
221
223 location = self.xmlelement.find(self.namespaced("location"))
224 if location is None:
225 return []
226 else:
227 return [':'.join([location.get("filename"), location.get("line")])]
228
229 - def merge(self, otherunit, overwrite=False, comments=True):
234
236 return self._gettype() == "obsolete"
237
238
240 """Class representing a XLIFF file store."""
241 UnitClass = tsunit
242 Name = _("Qt Linguist Translation File")
243 Mimetypes = ["application/x-linguist"]
244 Extensions = ["ts"]
245 rootNode = "TS"
246
247 bodyNode = "context"
248 XMLskeleton = '''<!DOCTYPE TS>
249 <TS>
250 </TS>
251 '''
252 namespace = ''
253
257
258 - def initbody(self):
259 """Initialises self.body."""
260 self.namespace = self.document.getroot().nsmap.get(None, None)
261 if self._contextname:
262 self.body = self.getcontextnode(self._contextname)
263 else:
264 self.body = self.document.getroot()
265
267 """Get the target language for this .ts file.
268
269 @return: ISO code e.g. af, fr, pt_BR
270 @rtype: String
271 """
272 return self.body.get('language')
273
275 """Set the target language for this .ts file to L{targetlanguage}.
276
277 @param targetlanguage: ISO code e.g. af, fr, pt_BR
278 @type targetlanguage: String
279 """
280 if targetlanguage:
281 self.body.set('language', targetlanguage)
282
283 - def _createcontext(self, contextname, comment=None):
284 """Creates a context node with an optional comment"""
285 context = etree.SubElement(self.document.getroot(), self.namespaced(self.bodyNode))
286 name = etree.SubElement(context, self.namespaced("name"))
287 name.text = contextname
288 if comment:
289 comment_node = context.SubElement(context, "comment")
290 comment_node.text = comment
291 return context
292
293 - def _getcontextname(self, contextnode):
294 """Returns the name of the given context node."""
295 return filenode.find(self.namespaced("name")).text
296
298 """Returns all contextnames in this TS file."""
299 contextnodes = self.document.findall(self.namespaced("context"))
300 contextnames = [self.getcontextname(contextnode) for contextnode in contextnodes]
301 return contextnames
302
303 - def _getcontextnode(self, contextname):
304 """Returns the context node with the given name."""
305 contextnodes = self.document.findall(self.namespaced("context"))
306 for contextnode in contextnodes:
307 if self.getcontextname(contextnode) == contextname:
308 return contextnode
309 return None
310
311 - def addunit(self, unit, new=True, contextname=None, createifmissing=False):
312 """Adds the given unit to the last used body node (current context).
313
314 If the contextname is specified, switch to that context (creating it
315 if allowed by createifmissing)."""
316 if self._contextname != contextname:
317 if not self._switchcontext(contextname, createifmissing):
318 return None
319 super(tsfile, self).addunit(unit, new)
320
321 return unit
322
323 - def _switchcontext(self, contextname, createifmissing=False):
324 """Switch the current context to the one named contextname, optionally
325 creating it if it doesn't exist."""
326 self._contextname = contextname
327 contextnode = self._getcontextnode(contextname)
328 if contextnode is None:
329 if not createifmissing:
330 return False
331 contextnode = self._createcontext(contextname)
332
333 self.body = contextnode
334 if self.body is None:
335 return False
336 return True
337
344
346 """Converts to a string containing the file's XML.
347
348 We have to override this to ensure mimic the Qt convention:
349 - no XML decleration
350 - plain DOCTYPE that lxml seems to ignore
351 """
352
353
354
355
356 output = etree.tostring(self.document, pretty_print=True,
357 xml_declaration=False, encoding='utf-8')
358 if not "<!DOCTYPE TS>" in output[:30]:
359 output = "<!DOCTYPE TS>" + output
360 return output
361