Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(60)

Side by Side Diff: grit/node/message.py

Issue 1442863002: Remove contents of grit's SVN repository. (Closed) Base URL: http://grit-i18n.googlecode.com/svn/trunk/
Patch Set: Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « grit/node/mapping.py ('k') | grit/node/message_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 '''Handling of the <message> element.
7 '''
8
9 import re
10 import types
11
12 from grit.node import base
13
14 import grit.format.rc_header
15 import grit.format.rc
16
17 from grit import clique
18 from grit import exception
19 from grit import lazy_re
20 from grit import tclib
21 from grit import util
22
23 # Finds whitespace at the start and end of a string which can be multiline.
24 _WHITESPACE = lazy_re.compile('(?P<start>\s*)(?P<body>.+?)(?P<end>\s*)\Z',
25 re.DOTALL | re.MULTILINE)
26
27
28 class MessageNode(base.ContentNode):
29 '''A <message> element.'''
30
31 # For splitting a list of things that can be separated by commas or
32 # whitespace
33 _SPLIT_RE = lazy_re.compile('\s*,\s*|\s+')
34
35 def __init__(self):
36 super(MessageNode, self).__init__()
37 # Valid after EndParsing, this is the MessageClique that contains the
38 # source message and any translations of it that have been loaded.
39 self.clique = None
40
41 # We don't send leading and trailing whitespace into the translation
42 # console, but rather tack it onto the source message and any
43 # translations when formatting them into RC files or what have you.
44 self.ws_at_start = '' # Any whitespace characters at the start of the text
45 self.ws_at_end = '' # --"-- at the end of the text
46
47 # A list of "shortcut groups" this message is in. We check to make sure
48 # that shortcut keys (e.g. &J) within each shortcut group are unique.
49 self.shortcut_groups_ = []
50
51 # Formatter-specific data used to control the output of individual strings.
52 # formatter_data is a space separated list of C preprocessor-style
53 # definitions. Names without values are given the empty string value.
54 # Example: "foo=5 bar baz=100"
55 self.formatter_data = {}
56
57 def _IsValidChild(self, child):
58 return isinstance(child, (PhNode))
59
60 def _IsValidAttribute(self, name, value):
61 if name not in ['name', 'offset', 'translateable', 'desc', 'meaning',
62 'internal_comment', 'shortcut_groups', 'custom_type',
63 'validation_expr', 'use_name_for_id', 'sub_variable',
64 'formatter_data']:
65 return False
66 if (name in ('translateable', 'sub_variable') and
67 value not in ['true', 'false']):
68 return False
69 return True
70
71 def MandatoryAttributes(self):
72 return ['name|offset']
73
74 def DefaultAttributes(self):
75 return {
76 'custom_type' : '',
77 'desc' : '',
78 'formatter_data' : '',
79 'internal_comment' : '',
80 'meaning' : '',
81 'shortcut_groups' : '',
82 'sub_variable' : 'false',
83 'translateable' : 'true',
84 'use_name_for_id' : 'false',
85 'validation_expr' : '',
86 }
87
88 def HandleAttribute(self, attrib, value):
89 base.ContentNode.HandleAttribute(self, attrib, value)
90 if attrib == 'formatter_data':
91 # Parse value, a space-separated list of defines, into a dict.
92 # Example: "foo=5 bar" -> {'foo':'5', 'bar':''}
93 for item in value.split():
94 name, sep, val = item.partition('=')
95 self.formatter_data[name] = val
96
97 def GetTextualIds(self):
98 '''
99 Returns the concatenation of the parent's node first_id and
100 this node's offset if it has one, otherwise just call the
101 superclass' implementation
102 '''
103 if 'offset' in self.attrs:
104 # we search for the first grouping node in the parents' list
105 # to take care of the case where the first parent is an <if> node
106 grouping_parent = self.parent
107 import grit.node.empty
108 while grouping_parent and not isinstance(grouping_parent,
109 grit.node.empty.GroupingNode):
110 grouping_parent = grouping_parent.parent
111
112 assert 'first_id' in grouping_parent.attrs
113 return [grouping_parent.attrs['first_id'] + '_' + self.attrs['offset']]
114 else:
115 return super(MessageNode, self).GetTextualIds()
116
117 def IsTranslateable(self):
118 return self.attrs['translateable'] == 'true'
119
120 def EndParsing(self):
121 super(MessageNode, self).EndParsing()
122
123 # Make the text (including placeholder references) and list of placeholders,
124 # then strip and store leading and trailing whitespace and create the
125 # tclib.Message() and a clique to contain it.
126
127 text = ''
128 placeholders = []
129 for item in self.mixed_content:
130 if isinstance(item, types.StringTypes):
131 text += item
132 else:
133 presentation = item.attrs['name'].upper()
134 text += presentation
135 ex = ' '
136 if len(item.children):
137 ex = item.children[0].GetCdata()
138 original = item.GetCdata()
139 placeholders.append(tclib.Placeholder(presentation, original, ex))
140
141 m = _WHITESPACE.match(text)
142 if m:
143 self.ws_at_start = m.group('start')
144 self.ws_at_end = m.group('end')
145 text = m.group('body')
146
147 self.shortcut_groups_ = self._SPLIT_RE.split(self.attrs['shortcut_groups'])
148 self.shortcut_groups_ = [i for i in self.shortcut_groups_ if i != '']
149
150 description_or_id = self.attrs['desc']
151 if description_or_id == '' and 'name' in self.attrs:
152 description_or_id = 'ID: %s' % self.attrs['name']
153
154 assigned_id = None
155 if self.attrs['use_name_for_id'] == 'true':
156 assigned_id = self.attrs['name']
157 message = tclib.Message(text=text, placeholders=placeholders,
158 description=description_or_id,
159 meaning=self.attrs['meaning'],
160 assigned_id=assigned_id)
161 self.InstallMessage(message)
162
163 def InstallMessage(self, message):
164 '''Sets this node's clique from a tclib.Message instance.
165
166 Args:
167 message: A tclib.Message.
168 '''
169 self.clique = self.UberClique().MakeClique(message, self.IsTranslateable())
170 for group in self.shortcut_groups_:
171 self.clique.AddToShortcutGroup(group)
172 if self.attrs['custom_type'] != '':
173 self.clique.SetCustomType(util.NewClassInstance(self.attrs['custom_type'],
174 clique.CustomType))
175 elif self.attrs['validation_expr'] != '':
176 self.clique.SetCustomType(
177 clique.OneOffCustomType(self.attrs['validation_expr']))
178
179 def SubstituteMessages(self, substituter):
180 '''Applies substitution to this message.
181
182 Args:
183 substituter: a grit.util.Substituter object.
184 '''
185 message = substituter.SubstituteMessage(self.clique.GetMessage())
186 if message is not self.clique.GetMessage():
187 self.InstallMessage(message)
188
189 def GetCliques(self):
190 if self.clique:
191 return [self.clique]
192 else:
193 return []
194
195 def Translate(self, lang):
196 '''Returns a translated version of this message.
197 '''
198 assert self.clique
199 msg = self.clique.MessageForLanguage(lang,
200 self.PseudoIsAllowed(),
201 self.ShouldFallbackToEnglish()
202 ).GetRealContent()
203 return msg.replace('[GRITLANGCODE]', lang)
204
205 def NameOrOffset(self):
206 if 'name' in self.attrs:
207 return self.attrs['name']
208 else:
209 return self.attrs['offset']
210
211 def ExpandVariables(self):
212 '''We always expand variables on Messages.'''
213 return True
214
215 def GetDataPackPair(self, lang, encoding):
216 '''Returns a (id, string) pair that represents the string id and the string
217 in the specified encoding, where |encoding| is one of the encoding values
218 accepted by util.Encode. This is used to generate the data pack data file.
219 '''
220 from grit.format import rc_header
221 id_map = rc_header.GetIds(self.GetRoot())
222 id = id_map[self.GetTextualIds()[0]]
223
224 message = self.ws_at_start + self.Translate(lang) + self.ws_at_end
225 return id, util.Encode(message, encoding)
226
227 def IsResourceMapSource(self):
228 return True
229
230 def GeneratesResourceMapEntry(self, output_all_resource_defines,
231 is_active_descendant):
232 return is_active_descendant
233
234 @staticmethod
235 def Construct(parent, message, name, desc='', meaning='', translateable=True):
236 '''Constructs a new message node that is a child of 'parent', with the
237 name, desc, meaning and translateable attributes set using the same-named
238 parameters and the text of the message and any placeholders taken from
239 'message', which must be a tclib.Message() object.'''
240 # Convert type to appropriate string
241 translateable = 'true' if translateable else 'false'
242
243 node = MessageNode()
244 node.StartParsing('message', parent)
245 node.HandleAttribute('name', name)
246 node.HandleAttribute('desc', desc)
247 node.HandleAttribute('meaning', meaning)
248 node.HandleAttribute('translateable', translateable)
249
250 items = message.GetContent()
251 for ix, item in enumerate(items):
252 if isinstance(item, types.StringTypes):
253 # Ensure whitespace at front and back of message is correctly handled.
254 if ix == 0:
255 item = "'''" + item
256 if ix == len(items) - 1:
257 item = item + "'''"
258
259 node.AppendContent(item)
260 else:
261 phnode = PhNode()
262 phnode.StartParsing('ph', node)
263 phnode.HandleAttribute('name', item.GetPresentation())
264 phnode.AppendContent(item.GetOriginal())
265
266 if len(item.GetExample()) and item.GetExample() != ' ':
267 exnode = ExNode()
268 exnode.StartParsing('ex', phnode)
269 exnode.AppendContent(item.GetExample())
270 exnode.EndParsing()
271 phnode.AddChild(exnode)
272
273 phnode.EndParsing()
274 node.AddChild(phnode)
275
276 node.EndParsing()
277 return node
278
279 class PhNode(base.ContentNode):
280 '''A <ph> element.'''
281
282 def _IsValidChild(self, child):
283 return isinstance(child, ExNode)
284
285 def MandatoryAttributes(self):
286 return ['name']
287
288 def EndParsing(self):
289 super(PhNode, self).EndParsing()
290 # We only allow a single example for each placeholder
291 if len(self.children) > 1:
292 raise exception.TooManyExamples()
293
294 def GetTextualIds(self):
295 # The 'name' attribute is not an ID.
296 return []
297
298
299 class ExNode(base.ContentNode):
300 '''An <ex> element.'''
301 pass
OLDNEW
« no previous file with comments | « grit/node/mapping.py ('k') | grit/node/message_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698