| OLD | NEW |
| (Empty) |
| 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 | |
| 7 list. | |
| 8 ''' | |
| 9 | |
| 10 import types | |
| 11 | |
| 12 from grit.gather import interface | |
| 13 from grit import clique | |
| 14 from grit import tclib | |
| 15 | |
| 16 | |
| 17 class 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 | |
| OLD | NEW |