Package translate :: Package storage :: Module ts2
[hide private]
[frames] | no frames]

Source Code for Module translate.storage.ts2

  1  #!/usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3  # 
  4  # Copyright 2008 Zuza Software Foundation 
  5  #  
  6  # This file is part of translate. 
  7  # 
  8  # translate is free software; you can redistribute it and/or modify 
  9  # it under the terms of the GNU General Public License as published by 
 10  # the Free Software Foundation; either version 2 of the License, or 
 11  # (at your option) any later version. 
 12  #  
 13  # translate is distributed in the hope that it will be useful, 
 14  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 16  # GNU General Public License for more details. 
 17  # 
 18  # You should have received a copy of the GNU General Public License 
 19  # along with translate; if not, write to the Free Software 
 20  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 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  # TODO: handle translation types 
 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   
62 -class tsunit(lisa.LISAunit):
63 """A single term in the xliff file.""" 64 65 rootNode = "message" 66 languageNode = "source" 67 textNode = "" 68 namespace = '' 69
70 - def createlanguageNode(self, lang, text, purpose):
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 #TODO: check language 78 # lisa.setXMLlang(langset, lang) 79 80 langset.text = text 81 return langset
82
83 - def _getsourcenode(self):
84 return self.xmlelement.find(self.namespaced(self.languageNode))
85
86 - def _gettargetnode(self):
87 return self.xmlelement.find(self.namespaced("translation"))
88
89 - def getlanguageNodes(self):
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
95 - def getsource(self):
96 sourcenode = self._getsourcenode() 97 if self.hasplural(): 98 return multistring([sourcenode.text]) 99 else: 100 return sourcenode.text
101 source = property(getsource, lisa.LISAunit.setsource) 102
103 - def settarget(self, text):
104 #Firstly deal with reinitialising to None or setting to identical string 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 #XXX: str vs unicode? 114 # text = data.forceunicode(text) 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
122 - def gettarget(self):
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
134 - def hasplural(self):
135 return self.xmlelement.get("numerus") == "yes"
136
137 - def addnote(self, text, origin=None):
138 """Add a note specifically in a "comment" tag""" 139 if isinstance(text, str): 140 text = text.decode("utf-8") 141 current_notes = self.getnotes(origin) 142 self.removenotes() 143 note = etree.SubElement(self.xmlelement, self.namespaced("comment")) 144 note.text = "\n".join(filter(None, [current_notes, text.strip()]))
145
146 - def getnotes(self, origin=None):
147 #TODO: consider only responding when origin has certain values 148 notenode = self.xmlelement.find(self.namespaced("comment")) 149 comment = '' 150 if not notenode is None: 151 comment = notenode.text 152 return comment
153
154 - def removenotes(self):
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
160 - def _gettype(self):
161 """Returns the type of this translation.""" 162 return self._gettargetnode().get("type")
163
164 - def _settype(self, value=None):
165 """Set the type of this translation.""" 166 if value is None and self._gettype: 167 # lxml recommends against using .attrib, but there seems to be no 168 # other way 169 self._gettargetnode().attrib.pop("type") 170 else: 171 self._gettargetnode().set("type", value)
172
173 - def isreview(self):
174 """States whether this unit needs to be reviewed""" 175 return self._gettype() == "unfinished"
176
177 - def isfuzzy(self):
178 return self._gettype() == "unfinished"
179
180 - def markfuzzy(self, value=True):
181 if value: 182 self._settype("unfinished") 183 else: 184 self._settype(None)
185
186 - def getid(self):
187 # return self._context_node.text + self.source 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
197 - def addlocation(self, location):
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
205 - def getlocations(self):
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):
213 super(tsunit, self).merge(otherunit, overwrite, comments) 214 #TODO: check if this is necessary: 215 if otherunit.isfuzzy(): 216 self.markfuzzy()
217
218 - def isobsolete(self):
219 return self._gettype() == "obsolete"
220 221 # def createfromxmlElement(cls, element): 222 # unit = lisa.LISAunit.createfromxmlElement(element) 223 # unit._context_node = 224 225
226 -class tsfile(lisa.LISAfile):
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 # We will switch out .body to fit with the context we are working on 234 bodyNode = "context" 235 XMLskeleton = '''<!DOCTYPE TS> 236 <TS> 237 </TS> 238 ''' 239 namespace = '' 240
241 - def __init__(self, *args, **kwargs):
242 self._contextname = None 243 lisa.LISAfile.__init__(self, *args, **kwargs)
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 # unit._context_node = self.getcontextnode(self._contextname) 289 # lisa.setXMLspace(unit.xmlelement, "preserve") 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
308 - def nplural(self):
309 lang = self.body.get("language") 310 if NPLURALS.has_key(lang): 311 return NPLURALS[lang] 312 else: 313 return 1
314