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

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

Issue 7994004: Initial source commit to grit-i18n project. (Closed) Base URL: http://grit-i18n.googlecode.com/svn/trunk/
Patch Set: Created 9 years, 2 months 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')
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 #!/usr/bin/python2.4
2 # Copyright (c) 2011 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 tclib
20 from grit import util
21
22 BINARY, UTF8, UTF16 = range(3)
23
24 # Finds whitespace at the start and end of a string which can be multiline.
25 _WHITESPACE = re.compile('(?P<start>\s*)(?P<body>.+?)(?P<end>\s*)\Z',
26 re.DOTALL | re.MULTILINE)
27
28
29 class MessageNode(base.ContentNode):
30 '''A <message> element.'''
31
32 # For splitting a list of things that can be separated by commas or
33 # whitespace
34 _SPLIT_RE = re.compile('\s*,\s*|\s+')
35
36 def __init__(self):
37 super(type(self), self).__init__()
38 # Valid after EndParsing, this is the MessageClique that contains the
39 # source message and any translations of it that have been loaded.
40 self.clique = None
41
42 # We don't send leading and trailing whitespace into the translation
43 # console, but rather tack it onto the source message and any
44 # translations when formatting them into RC files or what have you.
45 self.ws_at_start = '' # Any whitespace characters at the start of the text
46 self.ws_at_end = '' # --"-- at the end of the text
47
48 # A list of "shortcut groups" this message is in. We check to make sure
49 # that shortcut keys (e.g. &J) within each shortcut group are unique.
50 self.shortcut_groups_ = []
51
52 def _IsValidChild(self, child):
53 return isinstance(child, (PhNode))
54
55 def _IsValidAttribute(self, name, value):
56 if name not in ['name', 'offset', 'translateable', 'desc', 'meaning',
57 'internal_comment', 'shortcut_groups', 'custom_type',
58 'validation_expr', 'use_name_for_id']:
59 return False
60 if name == 'translateable' and value not in ['true', 'false']:
61 return False
62 return True
63
64 def MandatoryAttributes(self):
65 return ['name|offset']
66
67 def DefaultAttributes(self):
68 return {
69 'translateable' : 'true',
70 'desc' : '',
71 'meaning' : '',
72 'internal_comment' : '',
73 'shortcut_groups' : '',
74 'custom_type' : '',
75 'validation_expr' : '',
76 'use_name_for_id' : 'false',
77 }
78
79 def GetTextualIds(self):
80 '''
81 Returns the concatenation of the parent's node first_id and
82 this node's offset if it has one, otherwise just call the
83 superclass' implementation
84 '''
85 if 'offset' in self.attrs:
86 # we search for the first grouping node in the parents' list
87 # to take care of the case where the first parent is an <if> node
88 grouping_parent = self.parent
89 import grit.node.empty
90 while grouping_parent and not isinstance(grouping_parent,
91 grit.node.empty.GroupingNode):
92 grouping_parent = grouping_parent.parent
93
94 assert 'first_id' in grouping_parent.attrs
95 return [grouping_parent.attrs['first_id'] + '_' + self.attrs['offset']]
96 else:
97 return super(type(self), self).GetTextualIds()
98
99 def IsTranslateable(self):
100 return self.attrs['translateable'] == 'true'
101
102 def ItemFormatter(self, t):
103 # Only generate an output if the if condition is satisfied.
104 if not self.SatisfiesOutputCondition():
105 return super(type(self), self).ItemFormatter(t)
106
107 if t == 'rc_header':
108 return grit.format.rc_header.Item()
109 elif t in ('rc_all', 'rc_translateable', 'rc_nontranslateable'):
110 return grit.format.rc.Message()
111 elif t == 'js_map_format':
112 return grit.format.js_map_format.Message()
113 else:
114 return super(type(self), self).ItemFormatter(t)
115
116 def EndParsing(self):
117 super(type(self), self).EndParsing()
118
119 # Make the text (including placeholder references) and list of placeholders,
120 # then strip and store leading and trailing whitespace and create the
121 # tclib.Message() and a clique to contain it.
122
123 text = ''
124 placeholders = []
125 for item in self.mixed_content:
126 if isinstance(item, types.StringTypes):
127 text += item
128 else:
129 presentation = item.attrs['name'].upper()
130 text += presentation
131 ex = ' '
132 if len(item.children):
133 ex = item.children[0].GetCdata()
134 original = item.GetCdata()
135 placeholders.append(tclib.Placeholder(presentation, original, ex))
136
137 m = _WHITESPACE.match(text)
138 if m:
139 self.ws_at_start = m.group('start')
140 self.ws_at_end = m.group('end')
141 text = m.group('body')
142
143 self.shortcut_groups_ = self._SPLIT_RE.split(self.attrs['shortcut_groups'])
144 self.shortcut_groups_ = [i for i in self.shortcut_groups_ if i != '']
145
146 description_or_id = self.attrs['desc']
147 if description_or_id == '' and 'name' in self.attrs:
148 description_or_id = 'ID: %s' % self.attrs['name']
149
150 assigned_id = None
151 if (self.attrs['use_name_for_id'] == 'true' and
152 self.SatisfiesOutputCondition()):
153 assigned_id = self.attrs['name']
154 message = tclib.Message(text=text, placeholders=placeholders,
155 description=description_or_id,
156 meaning=self.attrs['meaning'],
157 assigned_id=assigned_id)
158 self.clique = self.UberClique().MakeClique(message, self.IsTranslateable())
159 for group in self.shortcut_groups_:
160 self.clique.AddToShortcutGroup(group)
161 if self.attrs['custom_type'] != '':
162 self.clique.SetCustomType(util.NewClassInstance(self.attrs['custom_type'],
163 clique.CustomType))
164 elif self.attrs['validation_expr'] != '':
165 self.clique.SetCustomType(
166 clique.OneOffCustomType(self.attrs['validation_expr']))
167
168 def GetCliques(self):
169 if self.clique:
170 return [self.clique]
171 else:
172 return []
173
174 def Translate(self, lang):
175 '''Returns a translated version of this message.
176 '''
177 assert self.clique
178 msg = self.clique.MessageForLanguage(lang,
179 self.PseudoIsAllowed(),
180 self.ShouldFallbackToEnglish()
181 ).GetRealContent()
182 return msg.replace('[GRITLANGCODE]', lang)
183
184 def NameOrOffset(self):
185 if 'name' in self.attrs:
186 return self.attrs['name']
187 else:
188 return self.attrs['offset']
189
190 def GetDataPackPair(self, lang, encoding):
191 '''Returns a (id, string) pair that represents the string id and the string
192 in utf8. This is used to generate the data pack data file.
193 '''
194 from grit.format import rc_header
195 id_map = rc_header.Item.tids_
196 id = id_map[self.GetTextualIds()[0]]
197
198 message = self.ws_at_start + self.Translate(lang) + self.ws_at_end
199 if "\\n" in message:
200 # Windows automatically translates \n to a new line, but GTK+ doesn't.
201 # Manually do the conversion here rather than at run time.
202 message = message.replace("\\n", "\n")
203 # |message| is a python unicode string, so convert to a byte stream that
204 # has the correct encoding requested for the datapacks. We skip the first
205 # 2 bytes of text resources because it is the BOM.
206 if encoding == UTF8:
207 return id, message.encode('utf8')
208 if encoding == UTF16:
209 return id, message.encode('utf16')[2:]
210 # Default is BINARY
211 return id, message
212
213 # static method
214 def Construct(parent, message, name, desc='', meaning='', translateable=True):
215 '''Constructs a new message node that is a child of 'parent', with the
216 name, desc, meaning and translateable attributes set using the same-named
217 parameters and the text of the message and any placeholders taken from
218 'message', which must be a tclib.Message() object.'''
219 # Convert type to appropriate string
220 if translateable:
221 translateable = 'true'
222 else:
223 translateable = 'false'
224
225 node = MessageNode()
226 node.StartParsing('message', parent)
227 node.HandleAttribute('name', name)
228 node.HandleAttribute('desc', desc)
229 node.HandleAttribute('meaning', meaning)
230 node.HandleAttribute('translateable', translateable)
231
232 items = message.GetContent()
233 for ix in range(len(items)):
234 if isinstance(items[ix], types.StringTypes):
235 text = items[ix]
236
237 # Ensure whitespace at front and back of message is correctly handled.
238 if ix == 0:
239 text = "'''" + text
240 if ix == len(items) - 1:
241 text = text + "'''"
242
243 node.AppendContent(text)
244 else:
245 phnode = PhNode()
246 phnode.StartParsing('ph', node)
247 phnode.HandleAttribute('name', items[ix].GetPresentation())
248 phnode.AppendContent(items[ix].GetOriginal())
249
250 if len(items[ix].GetExample()) and items[ix].GetExample() != ' ':
251 exnode = ExNode()
252 exnode.StartParsing('ex', phnode)
253 exnode.AppendContent(items[ix].GetExample())
254 exnode.EndParsing()
255 phnode.AddChild(exnode)
256
257 phnode.EndParsing()
258 node.AddChild(phnode)
259
260 node.EndParsing()
261 return node
262 Construct = staticmethod(Construct)
263
264 class PhNode(base.ContentNode):
265 '''A <ph> element.'''
266
267 def _IsValidChild(self, child):
268 return isinstance(child, ExNode)
269
270 def MandatoryAttributes(self):
271 return ['name']
272
273 def EndParsing(self):
274 super(type(self), self).EndParsing()
275 # We only allow a single example for each placeholder
276 if len(self.children) > 1:
277 raise exception.TooManyExamples()
278
279
280 class ExNode(base.ContentNode):
281 '''An <ex> element.'''
282 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