Home | History | Annotate | Download | only in gather
      1 #!/usr/bin/env python
      2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 '''Support for ALL_ALL.xml format used by Igoogle plug-ins in Google Desktop.'''
      7 
      8 import StringIO
      9 import re
     10 import xml.sax
     11 import xml.sax.handler
     12 import xml.sax.saxutils
     13 
     14 from grit.gather import regexp
     15 from grit import util
     16 from grit import tclib
     17 
     18 # Placeholders can be defined in strings.xml files by putting the name of the
     19 # placeholder between [![ and ]!] e.g. <MSG>Hello [![USER]!] how are you<MSG>
     20 PLACEHOLDER_RE = re.compile('(\[!\[|\]!\])')
     21 
     22 
     23 class IgoogleStringsContentHandler(xml.sax.handler.ContentHandler):
     24   '''A very dumb parser for splitting the strings.xml file into translateable
     25   and nontranslateable chunks.'''
     26 
     27   def __init__(self, parent):
     28     self.curr_elem = ''
     29     self.curr_text = ''
     30     self.parent = parent
     31     self.resource_name = ''
     32     self.meaning = ''
     33     self.translateable = True
     34 
     35   def startElement(self, name, attrs):
     36     if (name != 'messagebundle'):
     37       self.curr_elem = name
     38 
     39       attr_names = attrs.getQNames()
     40       if 'name' in attr_names:
     41         self.resource_name = attrs.getValueByQName('name')
     42 
     43       att_text = []
     44       for attr_name in attr_names:
     45         att_text.append(' ')
     46         att_text.append(attr_name)
     47         att_text.append('=')
     48         att_text.append(
     49           xml.sax.saxutils.quoteattr(attrs.getValueByQName(attr_name)))
     50 
     51       self.parent._AddNontranslateableChunk("<%s%s>" %
     52                                             (name, ''.join(att_text)))
     53 
     54   def characters(self, content):
     55     if self.curr_elem != '':
     56       self.curr_text += content
     57 
     58   def endElement(self, name):
     59     if name != 'messagebundle':
     60       self.parent.AddMessage(self.curr_text, self.resource_name,
     61                              self.meaning, self.translateable)
     62       self.parent._AddNontranslateableChunk("</%s>\n" % name)
     63       self.curr_elem = ''
     64       self.curr_text = ''
     65       self.resource_name = ''
     66       self.meaning = ''
     67       self.translateable = True
     68 
     69   def ignorableWhitespace(self, whitespace):
     70     pass
     71 
     72 
     73 class IgoogleStrings(regexp.RegexpGatherer):
     74   '''Supports the ALL_ALL.xml format used by Igoogle gadgets.'''
     75 
     76   def AddMessage(self, msgtext, description, meaning, translateable):
     77     if msgtext == '':
     78       return
     79 
     80     msg = tclib.Message(description=description, meaning=meaning)
     81 
     82     unescaped_text = self.UnEscape(msgtext)
     83     parts = PLACEHOLDER_RE.split(unescaped_text)
     84     in_placeholder = False
     85     for part in parts:
     86       if part == '':
     87         continue
     88       elif part == '[![':
     89         in_placeholder = True
     90       elif part == ']!]':
     91         in_placeholder = False
     92       else:
     93         if in_placeholder:
     94           msg.AppendPlaceholder(tclib.Placeholder(part, '[![%s]!]' % part,
     95                                                   '(placeholder)'))
     96         else:
     97           msg.AppendText(part)
     98 
     99     self.skeleton_.append(
    100       self.uberclique.MakeClique(msg, translateable=translateable))
    101 
    102     # if statement needed because this is supposed to be idempotent (so never
    103     # set back to false)
    104     if translateable:
    105       self.translatable_chunk_ = True
    106 
    107   # Although we use the RegexpGatherer base class, we do not use the
    108   # _RegExpParse method of that class to implement Parse().  Instead, we
    109   # parse using a SAX parser.
    110   def Parse(self):
    111     if self.have_parsed_:
    112       return
    113     self.have_parsed_ = True
    114 
    115     self.text_ = self._LoadInputFile().strip()
    116     self._AddNontranslateableChunk(u'<messagebundle>\n')
    117     stream = StringIO.StringIO(self.text_)
    118     handler = IgoogleStringsContentHandler(self)
    119     xml.sax.parse(stream, handler)
    120     self._AddNontranslateableChunk(u'</messagebundle>\n')
    121 
    122   def Escape(self, text):
    123     return util.EncodeCdata(text)
    124