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 8import StringIO 9import re 10import xml.sax 11import xml.sax.handler 12import xml.sax.saxutils 13 14from grit.gather import regexp 15from grit import util 16from 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> 20PLACEHOLDER_RE = re.compile('(\[!\[|\]!\])') 21 22 23class 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 73class 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