195640e3a20adea634b4df4ccf8c93f411184c438joi@chromium.org#!/usr/bin/env python 295640e3a20adea634b4df4ccf8c93f411184c438joi@chromium.org# Copyright (c) 2012 The Chromium Authors. All rights reserved. 301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# Use of this source code is governed by a BSD-style license that can be 401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# found in the LICENSE file. 501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org'''Support for "strings.xml" format used by Muppet plug-ins in Google Desktop.''' 701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport StringIO 901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport xml.sax 1001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport xml.sax.handler 1101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport xml.sax.saxutils 1201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 1301fadb72b6e94e6511eaffd1874a8cc095f098a7joi@chromium.orgfrom grit import lazy_re 1401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit import tclib 15ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.orgfrom grit import util 16b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.orgfrom grit.gather import regexp 17b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org 1801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 1901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# Placeholders can be defined in strings.xml files by putting the name of the 2001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# placeholder between [![ and ]!] e.g. <MSG>Hello [![USER]!] how are you<MSG> 2101fadb72b6e94e6511eaffd1874a8cc095f098a7joi@chromium.orgPLACEHOLDER_RE = lazy_re.compile('(\[!\[|\]!\])') 2201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 2301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 2401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass MuppetStringsContentHandler(xml.sax.handler.ContentHandler): 2501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''A very dumb parser for splitting the strings.xml file into translateable 2601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org and nontranslateable chunks.''' 2701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 2801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def __init__(self, parent): 2901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.curr_elem = '' 3001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.curr_text = '' 3101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.parent = parent 3201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.description = '' 3301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.meaning = '' 3401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.translateable = True 3501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 3601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def startElement(self, name, attrs): 3701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if (name != 'strings'): 3801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.curr_elem = name 3901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 4001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org attr_names = attrs.getQNames() 4101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if 'desc' in attr_names: 4201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.description = attrs.getValueByQName('desc') 4301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if 'meaning' in attr_names: 4401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.meaning = attrs.getValueByQName('meaning') 4501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if 'translateable' in attr_names: 4601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org value = attrs.getValueByQName('translateable') 4701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if value.lower() not in ['true', 'yes']: 4801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.translateable = False 4901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 5001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org att_text = [] 5101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for attr_name in attr_names: 5201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org att_text.append(' ') 5301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org att_text.append(attr_name) 5401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org att_text.append('=') 5501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org att_text.append( 5601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org xml.sax.saxutils.quoteattr(attrs.getValueByQName(attr_name))) 5701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 5801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.parent._AddNontranslateableChunk("<%s%s>" % 5901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org (name, ''.join(att_text))) 6001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 6101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def characters(self, content): 6201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if self.curr_elem != '': 6301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.curr_text += content 6401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 6501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def endElement(self, name): 6601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if name != 'strings': 6701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.parent.AddMessage(self.curr_text, self.description, 6801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.meaning, self.translateable) 6901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.parent._AddNontranslateableChunk("</%s>\n" % name) 7001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.curr_elem = '' 7101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.curr_text = '' 7201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.description = '' 7301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.meaning = '' 7401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.translateable = True 7501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 7601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def ignorableWhitespace(self, whitespace): 7701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org pass 7801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 7901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass MuppetStrings(regexp.RegexpGatherer): 8001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Supports the strings.xml format used by Muppet gadgets.''' 8101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 8201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def AddMessage(self, msgtext, description, meaning, translateable): 8301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if msgtext == '': 8401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return 8501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 8601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org msg = tclib.Message(description=description, meaning=meaning) 8701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 8801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org unescaped_text = self.UnEscape(msgtext) 8901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org parts = PLACEHOLDER_RE.split(unescaped_text) 9001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org in_placeholder = False 9101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for part in parts: 9201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if part == '': 9301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org continue 9401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org elif part == '[![': 9501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org in_placeholder = True 9601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org elif part == ']!]': 9701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org in_placeholder = False 9801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org else: 9901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if in_placeholder: 10001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org msg.AppendPlaceholder(tclib.Placeholder(part, '[![%s]!]' % part, 10101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '(placeholder)')) 10201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org else: 10301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org msg.AppendText(part) 10401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 10501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.skeleton_.append( 10601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.uberclique.MakeClique(msg, translateable=translateable)) 10701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 10801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # if statement needed because this is supposed to be idempotent (so never 10901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # set back to false) 11001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if translateable: 11101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.translatable_chunk_ = True 11201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 11301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # Although we use the RegexpGatherer base class, we do not use the 11401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # _RegExpParse method of that class to implement Parse(). Instead, we 11501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # parse using a SAX parser. 11601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def Parse(self): 117ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org if self.have_parsed_: 11801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return 11901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.have_parsed_ = True 120ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org 121b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org text = self._LoadInputFile().encode(self.encoding) 122ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org if util.IsExtraVerbose(): 123ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org print text 124ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org self.text_ = text.strip() 125ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org 12601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self._AddNontranslateableChunk(u'<strings>\n') 12701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org stream = StringIO.StringIO(self.text_) 12801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org handler = MuppetStringsContentHandler(self) 12901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org xml.sax.parse(stream, handler) 13001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self._AddNontranslateableChunk(u'</strings>\n') 13101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 13201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def Escape(self, text): 13301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return util.EncodeCdata(text) 134