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 |