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'''A baseclass for simple gatherers that store their gathered resource in a 7list. 8''' 9 10import types 11 12from grit.gather import interface 13from grit import clique 14from grit import tclib 15 16 17class SkeletonGatherer(interface.GathererBase): 18 '''Common functionality of gatherers that parse their input as a skeleton of 19 translatable and nontranslatable chunks. 20 ''' 21 22 def __init__(self, *args, **kwargs): 23 super(SkeletonGatherer, self).__init__(*args, **kwargs) 24 # List of parts of the document. Translateable parts are 25 # clique.MessageClique objects, nontranslateable parts are plain strings. 26 # Translated messages are inserted back into the skeleton using the quoting 27 # rules defined by self.Escape() 28 self.skeleton_ = [] 29 # A list of the names of IDs that need to be defined for this resource 30 # section to compile correctly. 31 self.ids_ = [] 32 # True if Parse() has already been called. 33 self.have_parsed_ = False 34 # True if a translatable chunk has been added 35 self.translatable_chunk_ = False 36 # If not None, all parts of the document will be put into this single 37 # message; otherwise the normal skeleton approach is used. 38 self.single_message_ = None 39 # Number to use for the next placeholder name. Used only if single_message 40 # is not None 41 self.ph_counter_ = 1 42 43 def GetText(self): 44 '''Returns the original text of the section''' 45 return self.text_ 46 47 def Escape(self, text): 48 '''Subclasses can override. Base impl is identity. 49 ''' 50 return text 51 52 def UnEscape(self, text): 53 '''Subclasses can override. Base impl is identity. 54 ''' 55 return text 56 57 def GetTextualIds(self): 58 '''Returns the list of textual IDs that need to be defined for this 59 resource section to compile correctly.''' 60 return self.ids_ 61 62 def _AddTextualId(self, id): 63 self.ids_.append(id) 64 65 def GetCliques(self): 66 '''Returns the message cliques for each translateable message in the 67 resource section.''' 68 return [x for x in self.skeleton_ if isinstance(x, clique.MessageClique)] 69 70 def Translate(self, lang, pseudo_if_not_available=True, 71 skeleton_gatherer=None, fallback_to_english=False): 72 if len(self.skeleton_) == 0: 73 raise exception.NotReady() 74 if skeleton_gatherer: 75 assert len(skeleton_gatherer.skeleton_) == len(self.skeleton_) 76 77 out = [] 78 for ix in range(len(self.skeleton_)): 79 if isinstance(self.skeleton_[ix], types.StringTypes): 80 if skeleton_gatherer: 81 # Make sure the skeleton is like the original 82 assert(isinstance(skeleton_gatherer.skeleton_[ix], types.StringTypes)) 83 out.append(skeleton_gatherer.skeleton_[ix]) 84 else: 85 out.append(self.skeleton_[ix]) 86 else: 87 if skeleton_gatherer: # Make sure the skeleton is like the original 88 assert(not isinstance(skeleton_gatherer.skeleton_[ix], 89 types.StringTypes)) 90 msg = self.skeleton_[ix].MessageForLanguage(lang, 91 pseudo_if_not_available, 92 fallback_to_english) 93 94 def MyEscape(text): 95 return self.Escape(text) 96 text = msg.GetRealContent(escaping_function=MyEscape) 97 out.append(text) 98 return ''.join(out) 99 100 def Parse(self): 101 '''Parses the section. Implemented by subclasses. Idempotent.''' 102 raise NotImplementedError() 103 104 def _AddNontranslateableChunk(self, chunk): 105 '''Adds a nontranslateable chunk.''' 106 if self.single_message_: 107 ph = tclib.Placeholder('XX%02dXX' % self.ph_counter_, chunk, chunk) 108 self.ph_counter_ += 1 109 self.single_message_.AppendPlaceholder(ph) 110 else: 111 self.skeleton_.append(chunk) 112 113 def _AddTranslateableChunk(self, chunk): 114 '''Adds a translateable chunk. It will be unescaped before being added.''' 115 # We don't want empty messages since they are redundant and the TC 116 # doesn't allow them. 117 if chunk == '': 118 return 119 120 unescaped_text = self.UnEscape(chunk) 121 if self.single_message_: 122 self.single_message_.AppendText(unescaped_text) 123 else: 124 self.skeleton_.append(self.uberclique.MakeClique( 125 tclib.Message(text=unescaped_text))) 126 self.translatable_chunk_ = True 127 128 def SubstituteMessages(self, substituter): 129 '''Applies substitutions to all messages in the tree. 130 131 Goes through the skeleton and finds all MessageCliques. 132 133 Args: 134 substituter: a grit.util.Substituter object. 135 ''' 136 if self.single_message_: 137 self.single_message_ = substituter.SubstituteMessage(self.single_message_) 138 new_skel = [] 139 for chunk in self.skeleton_: 140 if isinstance(chunk, clique.MessageClique): 141 old_message = chunk.GetMessage() 142 new_message = substituter.SubstituteMessage(old_message) 143 if new_message is not old_message: 144 new_skel.append(self.uberclique.MakeClique(new_message)) 145 continue 146 new_skel.append(chunk) 147 self.skeleton_ = new_skel 148