summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/tools/grit/grit/tool/transl2tc.py
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/tools/grit/grit/tool/transl2tc.py')
-rw-r--r--third_party/libwebrtc/tools/grit/grit/tool/transl2tc.py251
1 files changed, 251 insertions, 0 deletions
diff --git a/third_party/libwebrtc/tools/grit/grit/tool/transl2tc.py b/third_party/libwebrtc/tools/grit/grit/tool/transl2tc.py
new file mode 100644
index 0000000000..45301bbf58
--- /dev/null
+++ b/third_party/libwebrtc/tools/grit/grit/tool/transl2tc.py
@@ -0,0 +1,251 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+'''The 'grit transl2tc' tool.
+'''
+
+from __future__ import print_function
+
+from grit import grd_reader
+from grit import util
+from grit.tool import interface
+from grit.tool import rc2grd
+
+from grit.extern import tclib
+
+
+class TranslationToTc(interface.Tool):
+ '''A tool for importing existing translations in RC format into the
+Translation Console.
+
+Usage:
+
+grit -i GRD transl2tc [-l LIMITS] [RCOPTS] SOURCE_RC TRANSLATED_RC OUT_FILE
+
+The tool needs a "source" RC file, i.e. in English, and an RC file that is a
+translation of precisely the source RC file (not of an older or newer version).
+
+The tool also requires you to provide a .grd file (input file) e.g. using the
+-i global option or the GRIT_INPUT environment variable. The tool uses
+information from your .grd file to correct placeholder names in the
+translations and ensure that only translatable items and translations still
+being used are output.
+
+This tool will accept all the same RCOPTS as the 'grit rc2grd' tool. To get
+a list of these options, run 'grit help rc2grd'.
+
+Additionally, you can use the -l option (which must be the first option to the
+tool) to specify a file containing a list of message IDs to which output should
+be limited. This is only useful if you are limiting the output to your XMB
+files using the 'grit xmb' tool's -l option. See 'grit help xmb' for how to
+generate a file containing a list of the message IDs in an XMB file.
+
+The tool will scan through both of the RC files as well as any HTML files they
+refer to, and match together the source messages and translated messages. It
+will output a file (OUTPUT_FILE) you can import directly into the TC using the
+Bulk Translation Upload tool.
+'''
+
+ def ShortDescription(self):
+ return 'Import existing translations in RC format into the TC'
+
+ def Setup(self, globopt, args):
+ '''Sets the instance up for use.
+ '''
+ self.SetOptions(globopt)
+ self.rc2grd = rc2grd.Rc2Grd()
+ self.rc2grd.SetOptions(globopt)
+ self.limits = None
+ if len(args) and args[0] == '-l':
+ self.limits = util.ReadFile(args[1], 'utf-8').splitlines()
+ args = args[2:]
+ return self.rc2grd.ParseOptions(args, help_func=self.ShowUsage)
+
+ def Run(self, globopt, args):
+ args = self.Setup(globopt, args)
+
+ if len(args) != 3:
+ self.Out('This tool takes exactly three arguments:\n'
+ ' 1. The path to the original RC file\n'
+ ' 2. The path to the translated RC file\n'
+ ' 3. The output file path.\n')
+ return 2
+
+ grd = grd_reader.Parse(self.o.input, debug=self.o.extra_verbose)
+ grd.RunGatherers()
+
+ source_rc = util.ReadFile(args[0], self.rc2grd.input_encoding)
+ transl_rc = util.ReadFile(args[1], self.rc2grd.input_encoding)
+ translations = self.ExtractTranslations(grd,
+ source_rc, args[0],
+ transl_rc, args[1])
+
+ with util.WrapOutputStream(open(args[2], 'wb')) as output_file:
+ self.WriteTranslations(output_file, translations.items())
+
+ self.Out('Wrote output file %s' % args[2])
+
+ def ExtractTranslations(self, current_grd, source_rc, source_path,
+ transl_rc, transl_path):
+ '''Extracts translations from the translated RC file, matching them with
+ translations in the source RC file to calculate their ID, and correcting
+ placeholders, limiting output to translateables, etc. using the supplied
+ .grd file which is the current .grd file for your project.
+
+ If this object's 'limits' attribute is not None but a list, the output of
+ this function will be further limited to include only messages that have
+ message IDs in the 'limits' list.
+
+ Args:
+ current_grd: grit.node.base.Node child, that has had RunGatherers() run
+ on it
+ source_rc: Complete text of source RC file
+ source_path: Path to the source RC file
+ transl_rc: Complete text of translated RC file
+ transl_path: Path to the translated RC file
+
+ Return:
+ { id1 : text1, '12345678' : 'Hello USERNAME, howzit?' }
+ '''
+ source_grd = self.rc2grd.Process(source_rc, source_path)
+ self.VerboseOut('Read %s into GRIT format, running gatherers.\n' % source_path)
+ source_grd.SetOutputLanguage(current_grd.output_language)
+ source_grd.SetDefines(current_grd.defines)
+ source_grd.RunGatherers(debug=self.o.extra_verbose)
+ transl_grd = self.rc2grd.Process(transl_rc, transl_path)
+ transl_grd.SetOutputLanguage(current_grd.output_language)
+ transl_grd.SetDefines(current_grd.defines)
+ self.VerboseOut('Read %s into GRIT format, running gatherers.\n' % transl_path)
+ transl_grd.RunGatherers(debug=self.o.extra_verbose)
+ self.VerboseOut('Done running gatherers for %s.\n' % transl_path)
+
+ # Proceed to create a map from ID to translation, getting the ID from the
+ # source GRD and the translation from the translated GRD.
+ id2transl = {}
+ for source_node in source_grd:
+ source_cliques = source_node.GetCliques()
+ if not len(source_cliques):
+ continue
+
+ assert 'name' in source_node.attrs, 'All nodes with cliques should have an ID'
+ node_id = source_node.attrs['name']
+ self.ExtraVerboseOut('Processing node %s\n' % node_id)
+ transl_node = transl_grd.GetNodeById(node_id)
+
+ if transl_node:
+ transl_cliques = transl_node.GetCliques()
+ if not len(transl_cliques) == len(source_cliques):
+ self.Out(
+ 'Warning: Translation for %s has wrong # of cliques, skipping.\n' %
+ node_id)
+ continue
+ else:
+ self.Out('Warning: No translation for %s, skipping.\n' % node_id)
+ continue
+
+ if source_node.name == 'message':
+ # Fixup placeholders as well as possible based on information from
+ # the current .grd file if they are 'TODO_XXXX' placeholders. We need
+ # to fixup placeholders in the translated message so that it looks right
+ # and we also need to fixup placeholders in the source message so that
+ # its calculated ID will match the current message.
+ current_node = current_grd.GetNodeById(node_id)
+ if current_node:
+ assert len(source_cliques) == len(current_node.GetCliques()) == 1
+
+ source_msg = source_cliques[0].GetMessage()
+ current_msg = current_node.GetCliques()[0].GetMessage()
+
+ # Only do this for messages whose source version has not changed.
+ if (source_msg.GetRealContent() != current_msg.GetRealContent()):
+ self.VerboseOut('Info: Message %s has changed; skipping\n' % node_id)
+ else:
+ transl_msg = transl_cliques[0].GetMessage()
+ transl_content = transl_msg.GetContent()
+ current_content = current_msg.GetContent()
+ source_content = source_msg.GetContent()
+
+ ok_to_fixup = True
+ if (len(transl_content) != len(current_content)):
+ # message structure of translation is different, don't try fixup
+ ok_to_fixup = False
+ if ok_to_fixup:
+ for ix in range(len(transl_content)):
+ if isinstance(transl_content[ix], tclib.Placeholder):
+ if not isinstance(current_content[ix], tclib.Placeholder):
+ ok_to_fixup = False # structure has changed
+ break
+ if (transl_content[ix].GetOriginal() !=
+ current_content[ix].GetOriginal()):
+ ok_to_fixup = False # placeholders have likely been reordered
+ break
+ else: # translated part is not a placeholder but a string
+ if isinstance(current_content[ix], tclib.Placeholder):
+ ok_to_fixup = False # placeholders have likely been reordered
+ break
+
+ if not ok_to_fixup:
+ self.VerboseOut(
+ 'Info: Structure of message %s has changed; skipping.\n' % node_id)
+ else:
+ def Fixup(content, ix):
+ if (isinstance(content[ix], tclib.Placeholder) and
+ content[ix].GetPresentation().startswith('TODO_')):
+ assert isinstance(current_content[ix], tclib.Placeholder)
+ # Get the placeholder ID and example from the current message
+ content[ix] = current_content[ix]
+ for ix in range(len(transl_content)):
+ Fixup(transl_content, ix)
+ Fixup(source_content, ix)
+
+ # Only put each translation once into the map. Warn if translations
+ # for the same message are different.
+ for ix in range(len(transl_cliques)):
+ source_msg = source_cliques[ix].GetMessage()
+ source_msg.GenerateId() # needed to refresh ID based on new placeholders
+ message_id = source_msg.GetId()
+ translated_content = transl_cliques[ix].GetMessage().GetPresentableContent()
+
+ if message_id in id2transl:
+ existing_translation = id2transl[message_id]
+ if existing_translation != translated_content:
+ original_text = source_cliques[ix].GetMessage().GetPresentableContent()
+ self.Out('Warning: Two different translations for "%s":\n'
+ ' Translation 1: "%s"\n'
+ ' Translation 2: "%s"\n' %
+ (original_text, existing_translation, translated_content))
+ else:
+ id2transl[message_id] = translated_content
+
+ # Remove translations for messages that do not occur in the current .grd
+ # or have been marked as not translateable, or do not occur in the 'limits'
+ # list (if it has been set).
+ current_message_ids = current_grd.UberClique().AllMessageIds()
+ for message_id in list(id2transl.keys()):
+ if (message_id not in current_message_ids or
+ not current_grd.UberClique().BestClique(message_id).IsTranslateable() or
+ (self.limits and message_id not in self.limits)):
+ del id2transl[message_id]
+
+ return id2transl
+
+ @staticmethod
+ def WriteTranslations(output_file, translations):
+ '''Writes the provided list of translations to the provided output file
+ in the format used by the TC's Bulk Translation Upload tool. The file
+ must be UTF-8 encoded.
+
+ Args:
+ output_file: util.WrapOutputStream(open('bingo.out', 'wb'))
+ translations: [ [id1, text1], ['12345678', 'Hello USERNAME, howzit?'] ]
+
+ Return:
+ None
+ '''
+ for id, text in translations:
+ text = text.replace('<', '&lt;').replace('>', '&gt;')
+ output_file.write(id)
+ output_file.write(' ')
+ output_file.write(text)
+ output_file.write('\n')