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

Side by Side Diff: grit/tool/transl2tc.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, 3 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/tool/toolbar_preprocess.py ('k') | grit/tool/transl2tc_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) 2006-2008 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 '''The 'grit transl2tc' tool.
7 '''
8
9
10 import getopt
11
12 from grit.tool import interface
13 from grit.tool import rc2grd
14 from grit import grd_reader
15 from grit import util
16
17 from grit.extern import tclib
18
19
20 class TranslationToTc(interface.Tool):
21 '''A tool for importing existing translations in RC format into the
22 Translation Console.
23
24 Usage:
25
26 grit -i GRD transl2tc [-l LIMITS] [RCOPTS] SOURCE_RC TRANSLATED_RC OUT_FILE
27
28 The tool needs a "source" RC file, i.e. in English, and an RC file that is a
29 translation of precisely the source RC file (not of an older or newer version).
30
31 The tool also requires you to provide a .grd file (input file) e.g. using the
32 -i global option or the GRIT_INPUT environment variable. The tool uses
33 information from your .grd file to correct placeholder names in the
34 translations and ensure that only translatable items and translations still
35 being used are output.
36
37 This tool will accept all the same RCOPTS as the 'grit rc2grd' tool. To get
38 a list of these options, run 'grit help rc2grd'.
39
40 Additionally, you can use the -l option (which must be the first option to the
41 tool) to specify a file containing a list of message IDs to which output should
42 be limited. This is only useful if you are limiting the output to your XMB
43 files using the 'grit xmb' tool's -l option. See 'grit help xmb' for how to
44 generate a file containing a list of the message IDs in an XMB file.
45
46 The tool will scan through both of the RC files as well as any HTML files they
47 refer to, and match together the source messages and translated messages. It
48 will output a file (OUTPUT_FILE) you can import directly into the TC using the
49 Bulk Translation Upload tool.
50 '''
51
52 def ShortDescription(self):
53 return 'Import existing translations in RC format into the TC'
54
55 def Setup(self, globopt, args):
56 '''Sets the instance up for use.
57 '''
58 self.SetOptions(globopt)
59 self.rc2grd = rc2grd.Rc2Grd()
60 self.rc2grd.SetOptions(globopt)
61 self.limits = None
62 if len(args) and args[0] == '-l':
63 limit_file = file(args[1])
64 self.limits = limit_file.read().split('\n')
65 limit_file.close()
66 args = args[2:]
67 return self.rc2grd.ParseOptions(args)
68
69 def Run(self, globopt, args):
70 args = self.Setup(globopt, args)
71
72 if len(args) != 3:
73 self.Out('This tool takes exactly three arguments:\n'
74 ' 1. The path to the original RC file\n'
75 ' 2. The path to the translated RC file\n'
76 ' 3. The output file path.\n')
77 return 2
78
79 grd = grd_reader.Parse(self.o.input, debug=self.o.extra_verbose)
80 grd.RunGatherers(recursive = True)
81
82 source_rc = util.WrapInputStream(file(args[0], 'r'), self.rc2grd.input_encod ing)
83 transl_rc = util.WrapInputStream(file(args[1], 'r'), self.rc2grd.input_encod ing)
84 translations = self.ExtractTranslations(grd,
85 source_rc.read(), args[0],
86 transl_rc.read(), args[1])
87 transl_rc.close()
88 source_rc.close()
89
90 output_file = util.WrapOutputStream(file(args[2], 'w'))
91 self.WriteTranslations(output_file, translations.items())
92 output_file.close()
93
94 self.Out('Wrote output file %s' % args[2])
95
96 def ExtractTranslations(self, current_grd, source_rc, source_path, transl_rc, transl_path):
97 '''Extracts translations from the translated RC file, matching them with
98 translations in the source RC file to calculate their ID, and correcting
99 placeholders, limiting output to translateables, etc. using the supplied
100 .grd file which is the current .grd file for your project.
101
102 If this object's 'limits' attribute is not None but a list, the output of
103 this function will be further limited to include only messages that have
104 message IDs in the 'limits' list.
105
106 Args:
107 current_grd: grit.node.base.Node child, that has had RunGatherers(True) ru n on it
108 source_rc: Complete text of source RC file
109 source_path: Path to the source RC file
110 transl_rc: Complete text of translated RC file
111 transl_path: Path to the translated RC file
112
113 Return:
114 { id1 : text1, '12345678' : 'Hello USERNAME, howzit?' }
115 '''
116 source_grd = self.rc2grd.Process(source_rc, source_path)
117 self.VerboseOut('Read %s into GRIT format, running gatherers.\n' % source_pa th)
118 source_grd.RunGatherers(recursive=True, debug=self.o.extra_verbose)
119 transl_grd = self.rc2grd.Process(transl_rc, transl_path)
120 self.VerboseOut('Read %s into GRIT format, running gatherers.\n' % transl_pa th)
121 transl_grd.RunGatherers(recursive=True, debug=self.o.extra_verbose)
122 self.VerboseOut('Done running gatherers for %s.\n' % transl_path)
123
124 # Proceed to create a map from ID to translation, getting the ID from the
125 # source GRD and the translation from the translated GRD.
126 id2transl = {}
127 for source_node in source_grd:
128 source_cliques = source_node.GetCliques()
129 if not len(source_cliques):
130 continue
131
132 assert 'name' in source_node.attrs, 'All nodes with cliques should have an ID'
133 node_id = source_node.attrs['name']
134 self.ExtraVerboseOut('Processing node %s\n' % node_id)
135 transl_node = transl_grd.GetNodeById(node_id)
136
137 if transl_node:
138 transl_cliques = transl_node.GetCliques()
139 if not len(transl_cliques) == len(source_cliques):
140 self.Out(
141 'Warning: Translation for %s has wrong # of cliques, skipping.\n' %
142 node_id)
143 continue
144 else:
145 self.Out('Warning: No translation for %s, skipping.\n' % node_id)
146 continue
147
148 if source_node.name == 'message':
149 # Fixup placeholders as well as possible based on information from
150 # the current .grd file if they are 'TODO_XXXX' placeholders. We need
151 # to fixup placeholders in the translated message so that it looks right
152 # and we also need to fixup placeholders in the source message so that
153 # its calculated ID will match the current message.
154 current_node = current_grd.GetNodeById(node_id)
155 if current_node:
156 assert len(source_cliques) == 1 and len(current_node.GetCliques()) == 1
157
158 source_msg = source_cliques[0].GetMessage()
159 current_msg = current_node.GetCliques()[0].GetMessage()
160
161 # Only do this for messages whose source version has not changed.
162 if (source_msg.GetRealContent() != current_msg.GetRealContent()):
163 self.VerboseOut('Info: Message %s has changed; skipping\n' % node_id )
164 else:
165 transl_msg = transl_cliques[0].GetMessage()
166 transl_content = transl_msg.GetContent()
167 current_content = current_msg.GetContent()
168 source_content = source_msg.GetContent()
169
170 ok_to_fixup = True
171 if (len(transl_content) != len(current_content)):
172 # message structure of translation is different, don't try fixup
173 ok_to_fixup = False
174 if ok_to_fixup:
175 for ix in range(len(transl_content)):
176 if isinstance(transl_content[ix], tclib.Placeholder):
177 if not isinstance(current_content[ix], tclib.Placeholder):
178 ok_to_fixup = False # structure has changed
179 break
180 if (transl_content[ix].GetOriginal() !=
181 current_content[ix].GetOriginal()):
182 ok_to_fixup = False # placeholders have likely been reorder ed
183 break
184 else: # translated part is not a placeholder but a string
185 if isinstance(current_content[ix], tclib.Placeholder):
186 ok_to_fixup = False # placeholders have likely been reorder ed
187 break
188
189 if not ok_to_fixup:
190 self.VerboseOut(
191 'Info: Structure of message %s has changed; skipping.\n' % node_ id)
192 else:
193 def Fixup(content, ix):
194 if (isinstance(content[ix], tclib.Placeholder) and
195 content[ix].GetPresentation().startswith('TODO_')):
196 assert isinstance(current_content[ix], tclib.Placeholder)
197 # Get the placeholder ID and example from the current message
198 content[ix] = current_content[ix]
199 for ix in range(len(transl_content)):
200 Fixup(transl_content, ix)
201 Fixup(source_content, ix)
202
203 # Only put each translation once into the map. Warn if translations
204 # for the same message are different.
205 for ix in range(len(transl_cliques)):
206 source_msg = source_cliques[ix].GetMessage()
207 source_msg.GenerateId() # needed to refresh ID based on new placeholder s
208 message_id = source_msg.GetId()
209 translated_content = transl_cliques[ix].GetMessage().GetPresentableConte nt()
210
211 if message_id in id2transl:
212 existing_translation = id2transl[message_id]
213 if existing_translation != translated_content:
214 original_text = source_cliques[ix].GetMessage().GetPresentableConten t()
215 self.Out('Warning: Two different translations for "%s":\n'
216 ' Translation 1: "%s"\n'
217 ' Translation 2: "%s"\n' %
218 (original_text, existing_translation, translated_content))
219 else:
220 id2transl[message_id] = translated_content
221
222 # Remove translations for messages that do not occur in the current .grd
223 # or have been marked as not translateable, or do not occur in the 'limits'
224 # list (if it has been set).
225 current_message_ids = current_grd.UberClique().AllMessageIds()
226 for message_id in id2transl.keys():
227 if (message_id not in current_message_ids or
228 not current_grd.UberClique().BestClique(message_id).IsTranslateable() or
229 (self.limits and message_id not in self.limits)):
230 del id2transl[message_id]
231
232 return id2transl
233
234 # static method
235 def WriteTranslations(output_file, translations):
236 '''Writes the provided list of translations to the provided output file
237 in the format used by the TC's Bulk Translation Upload tool. The file
238 must be UTF-8 encoded.
239
240 Args:
241 output_file: util.WrapOutputStream(file('bingo.out', 'w'))
242 translations: [ [id1, text1], ['12345678', 'Hello USERNAME, howzit?'] ]
243
244 Return:
245 None
246 '''
247 for id, text in translations:
248 text = text.replace('<', '&lt;').replace('>', '&gt;')
249 output_file.write(id)
250 output_file.write(' ')
251 output_file.write(text)
252 output_file.write('\n')
253 WriteTranslations = staticmethod(WriteTranslations)
254
OLDNEW
« no previous file with comments | « grit/tool/toolbar_preprocess.py ('k') | grit/tool/transl2tc_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698