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