195640e3a20adea634b4df4ccf8c93f411184c438joi@chromium.org#!/usr/bin/env python 295640e3a20adea634b4df4ccf8c93f411184c438joi@chromium.org# Copyright (c) 2012 The Chromium Authors. All rights reserved. 301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# Use of this source code is governed by a BSD-style license that can be 401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# found in the LICENSE file. 501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org'''Collections of messages and their translations, called cliques. Also 701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgcollections of cliques (uber-cliques). 801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org''' 901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 1078ea93aa6fc9476de2a2bd3b3d337190275bcbdcjoi@chromium.orgimport re 1101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport types 1201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 1301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit import constants 1401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit import exception 151eddb32f808ac5930f24b1954b5c949b273a3869joi@chromium.orgfrom grit import lazy_re 1601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit import pseudo 1701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit import pseudo_rtl 1801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit import tclib 1901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 2001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 2101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass UberClique(object): 2201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''A factory (NOT a singleton factory) for making cliques. It has several 2301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org methods for working with the cliques created using the factory. 2401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 2501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 2601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def __init__(self): 2701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # A map from message ID to list of cliques whose source messages have 2801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # that ID. This will contain all cliques created using this factory. 2901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # Different messages can have the same ID because they have the 3001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # same translateable portion and placeholder names, but occur in different 3101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # places in the resource tree. 32834f421e536722c36c18a89469f367eb28868068joi@chromium.org # 33834f421e536722c36c18a89469f367eb28868068joi@chromium.org # Each list of cliques is kept sorted by description, to achieve 34834f421e536722c36c18a89469f367eb28868068joi@chromium.org # stable results from the BestClique method, see below. 3501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.cliques_ = {} 3601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 3701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # A map of clique IDs to list of languages to indicate translations where we 3801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # fell back to English. 3901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.fallback_translations_ = {} 4001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 4101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # A map of clique IDs to list of languages to indicate missing translations. 4201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.missing_translations_ = {} 4301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 4401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def _AddMissingTranslation(self, lang, clique, is_error): 4501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org tl = self.fallback_translations_ 4601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if is_error: 4701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org tl = self.missing_translations_ 4801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org id = clique.GetId() 4901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if id not in tl: 5001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org tl[id] = {} 5101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if lang not in tl[id]: 5201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org tl[id][lang] = 1 5301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 5401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def HasMissingTranslations(self): 5501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return len(self.missing_translations_) > 0 5601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 5701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def MissingTranslationsReport(self): 5801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns a string suitable for printing to report missing 5901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org and fallback translations to the user. 6001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 6101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def ReportTranslation(clique, langs): 6201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org text = clique.GetMessage().GetPresentableContent() 6378ea93aa6fc9476de2a2bd3b3d337190275bcbdcjoi@chromium.org # The text 'error' (usually 'Error:' but we are conservative) 6478ea93aa6fc9476de2a2bd3b3d337190275bcbdcjoi@chromium.org # can trigger some build environments (Visual Studio, we're 6578ea93aa6fc9476de2a2bd3b3d337190275bcbdcjoi@chromium.org # looking at you) to consider invocation of grit to have failed, 6678ea93aa6fc9476de2a2bd3b3d337190275bcbdcjoi@chromium.org # so we make sure never to output that word. 6778ea93aa6fc9476de2a2bd3b3d337190275bcbdcjoi@chromium.org extract = re.sub('(?i)error', 'REDACTED', text[0:40])[0:40] 6801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ellipsis = '' 6901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if len(text) > 40: 7001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ellipsis = '...' 7101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org langs_extract = langs[0:6] 7201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org describe_langs = ','.join(langs_extract) 7301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if len(langs) > 6: 7401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org describe_langs += " and %d more" % (len(langs) - 6) 7501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return " %s \"%s%s\" %s" % (clique.GetId(), extract, ellipsis, 7601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org describe_langs) 7701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org lines = [] 7801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if len(self.fallback_translations_): 7901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org lines.append( 8001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org "WARNING: Fell back to English for the following translations:") 8101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for (id, langs) in self.fallback_translations_.items(): 8201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org lines.append(ReportTranslation(self.cliques_[id][0], langs.keys())) 8301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if len(self.missing_translations_): 8401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org lines.append("ERROR: The following translations are MISSING:") 8501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for (id, langs) in self.missing_translations_.items(): 8601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org lines.append(ReportTranslation(self.cliques_[id][0], langs.keys())) 8701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return '\n'.join(lines) 8801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 8901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def MakeClique(self, message, translateable=True): 9001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Create a new clique initialized with a message. 9101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 9201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Args: 9301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org message: tclib.Message() 9401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org translateable: True | False 9501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 9601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org clique = MessageClique(self, message, translateable) 9701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 9801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # Enable others to find this clique by its message ID 9901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if message.GetId() in self.cliques_: 10001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org presentable_text = clique.GetMessage().GetPresentableContent() 101ca6c225d0059dd17b476c934e744a3c4f60e2df2joi@chromium.org if not message.HasAssignedId(): 102ca6c225d0059dd17b476c934e744a3c4f60e2df2joi@chromium.org for c in self.cliques_[message.GetId()]: 103ca6c225d0059dd17b476c934e744a3c4f60e2df2joi@chromium.org assert c.GetMessage().GetPresentableContent() == presentable_text 10401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.cliques_[message.GetId()].append(clique) 105834f421e536722c36c18a89469f367eb28868068joi@chromium.org # We need to keep each list of cliques sorted by description, to 106834f421e536722c36c18a89469f367eb28868068joi@chromium.org # achieve stable results from the BestClique method, see below. 107834f421e536722c36c18a89469f367eb28868068joi@chromium.org self.cliques_[message.GetId()].sort( 108834f421e536722c36c18a89469f367eb28868068joi@chromium.org key=lambda c:c.GetMessage().GetDescription()) 10901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org else: 11001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.cliques_[message.GetId()] = [clique] 11101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 11201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return clique 11301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 11401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def FindCliqueAndAddTranslation(self, translation, language): 11501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Adds the specified translation to the clique with the source message 11601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org it is a translation of. 11701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 11801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Args: 11901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org translation: tclib.Translation() 12001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org language: 'en' | 'fr' ... 12101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 12201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Return: 12301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org True if the source message was found, otherwise false. 12401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 12501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if translation.GetId() in self.cliques_: 12601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for clique in self.cliques_[translation.GetId()]: 12701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org clique.AddTranslation(translation, language) 12801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return True 12901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org else: 13001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return False 13101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 13201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def BestClique(self, id): 13301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns the "best" clique from a list of cliques. All the cliques 13401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org must have the same ID. The "best" clique is chosen in the following 13501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org order of preference: 136834f421e536722c36c18a89469f367eb28868068joi@chromium.org - The first clique that has a non-ID-based description. 137834f421e536722c36c18a89469f367eb28868068joi@chromium.org - If no such clique found, the first clique with an ID-based description. 138834f421e536722c36c18a89469f367eb28868068joi@chromium.org - Otherwise the first clique. 139834f421e536722c36c18a89469f367eb28868068joi@chromium.org 140834f421e536722c36c18a89469f367eb28868068joi@chromium.org This method is stable in terms of always returning a clique with 141834f421e536722c36c18a89469f367eb28868068joi@chromium.org an identical description (on different runs of GRIT on the same 142834f421e536722c36c18a89469f367eb28868068joi@chromium.org data) because self.cliques_ is sorted by description. 14301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 14401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org clique_list = self.cliques_[id] 145834f421e536722c36c18a89469f367eb28868068joi@chromium.org clique_with_id = None 146834f421e536722c36c18a89469f367eb28868068joi@chromium.org clique_default = None 14701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for clique in clique_list: 148834f421e536722c36c18a89469f367eb28868068joi@chromium.org if not clique_default: 149834f421e536722c36c18a89469f367eb28868068joi@chromium.org clique_default = clique 15001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 15101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org description = clique.GetMessage().GetDescription() 15201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if description and len(description) > 0: 15301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if not description.startswith('ID:'): 154834f421e536722c36c18a89469f367eb28868068joi@chromium.org # this is the preferred case so we exit right away 155834f421e536722c36c18a89469f367eb28868068joi@chromium.org return clique 156834f421e536722c36c18a89469f367eb28868068joi@chromium.org elif not clique_with_id: 157834f421e536722c36c18a89469f367eb28868068joi@chromium.org clique_with_id = clique 158834f421e536722c36c18a89469f367eb28868068joi@chromium.org if clique_with_id: 159834f421e536722c36c18a89469f367eb28868068joi@chromium.org return clique_with_id 160834f421e536722c36c18a89469f367eb28868068joi@chromium.org else: 161834f421e536722c36c18a89469f367eb28868068joi@chromium.org return clique_default 16201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 16301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def BestCliquePerId(self): 16401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Iterates over the list of all cliques and returns the best clique for 16501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org each ID. This will be the first clique with a source message that has a 16601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org non-empty description, or an arbitrary clique if none of them has a 16701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org description. 16801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 16901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for id in self.cliques_: 17001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org yield self.BestClique(id) 17101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 17201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def BestCliqueByOriginalText(self, text, meaning): 17301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Finds the "best" (as in BestClique()) clique that has original text 17401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 'text' and meaning 'meaning'. Returns None if there is no such clique. 17501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 17601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # If needed, this can be optimized by maintaining a map of 17701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # fingerprints of original text+meaning to cliques. 17801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for c in self.BestCliquePerId(): 17901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org msg = c.GetMessage() 18001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if msg.GetRealContent() == text and msg.GetMeaning() == meaning: 18101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return msg 18201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return None 18301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 18401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def AllMessageIds(self): 18501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns a list of all defined message IDs. 18601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 18701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return self.cliques_.keys() 18801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 18901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def AllCliques(self): 19001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Iterates over all cliques. Note that this can return multiple cliques 19101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org with the same ID. 19201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 19301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for cliques in self.cliques_.values(): 19401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for c in cliques: 19501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org yield c 19601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 19701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def GenerateXtbParserCallback(self, lang, debug=False): 19801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Creates a callback function as required by grit.xtb_reader.Parse(). 19901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org This callback will create Translation objects for each message from 20001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org the XTB that exists in this uberclique, and add them as translations for 20101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org the relevant cliques. The callback will add translations to the language 20201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org specified by 'lang' 20301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 20401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Args: 20501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org lang: 'fr' 20601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org debug: True | False 20701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 20801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def Callback(id, structure): 20901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if id not in self.cliques_: 21001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if debug: print "Ignoring translation #%s" % id 21101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return 21201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 21301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if debug: print "Adding translation #%s" % id 21401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 21501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # We fetch placeholder information from the original message (the XTB file 21601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # only contains placeholder names). 21701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org original_msg = self.BestClique(id).GetMessage() 21801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 21901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org translation = tclib.Translation(id=id) 22001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for is_ph,text in structure: 22101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if not is_ph: 22201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org translation.AppendText(text) 22301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org else: 22401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org found_placeholder = False 22501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for ph in original_msg.GetPlaceholders(): 22601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if ph.GetPresentation() == text: 22701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org translation.AppendPlaceholder(tclib.Placeholder( 22801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ph.GetPresentation(), ph.GetOriginal(), ph.GetExample())) 22901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org found_placeholder = True 23001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org break 23101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if not found_placeholder: 23201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org raise exception.MismatchingPlaceholders( 233bd79a1642abbe801db78778a59cdafc10e70bcccjoi@chromium.org 'Translation for message ID %s had <ph name="%s"/>, no match\n' 23401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 'in original message' % (id, text)) 23501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.FindCliqueAndAddTranslation(translation, lang) 23601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return Callback 23701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 23801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 23901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass CustomType(object): 24001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''A base class you should implement if you wish to specify a custom type 24101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for a message clique (i.e. custom validation and optional modification of 24201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org translations).''' 24301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 24401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def Validate(self, message): 24501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns true if the message (a tclib.Message object) is valid, 24601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org otherwise false. 24701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 24801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org raise NotImplementedError() 24901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 25001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def ValidateAndModify(self, lang, translation): 25101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns true if the translation (a tclib.Translation object) is valid, 25201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org otherwise false. The language is also passed in. This method may modify 25301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org the translation that is passed in, if it so wishes. 25401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 25501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org raise NotImplementedError() 25601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 25701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def ModifyTextPart(self, lang, text): 25801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''If you call ModifyEachTextPart, it will turn around and call this method 25901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for each text part of the translation. You should return the modified 26001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org version of the text, or just the original text to not change anything. 26101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 26201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org raise NotImplementedError() 26301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 26401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def ModifyEachTextPart(self, lang, translation): 26501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Call this to easily modify one or more of the textual parts of a 26601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org translation. It will call ModifyTextPart for each part of the 26701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org translation. 26801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 26901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org contents = translation.GetContent() 27001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for ix in range(len(contents)): 27101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if (isinstance(contents[ix], types.StringTypes)): 27201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org contents[ix] = self.ModifyTextPart(lang, contents[ix]) 27301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 27401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 27501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass OneOffCustomType(CustomType): 27601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''A very simple custom type that performs the validation expressed by 27701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org the input expression on all languages including the source language. 27801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org The expression can access the variables 'lang', 'msg' and 'text()' where 'lang' 27901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org is the language of 'msg', 'msg' is the message or translation being 28001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org validated and 'text()' returns the real contents of 'msg' (for shorthand). 28101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 28201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def __init__(self, expression): 28301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.expr = expression 28401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def Validate(self, message): 28501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return self.ValidateAndModify(MessageClique.source_language, message) 28601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def ValidateAndModify(self, lang, msg): 28701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def text(): 28801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return msg.GetRealContent() 28901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return eval(self.expr, {}, 29001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org {'lang' : lang, 29101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 'text' : text, 29201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 'msg' : msg, 29301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org }) 29401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 29501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 29601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass MessageClique(object): 29701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''A message along with all of its translations. Also code to bring 29801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org translations together with their original message.''' 29901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 30001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # change this to the language code of Messages you add to cliques_. 30101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # TODO(joi) Actually change this based on the <grit> node's source language 30201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org source_language = 'en' 30301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 30401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # A constant translation we use when asked for a translation into the 30501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # special language constants.CONSTANT_LANGUAGE. 30601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org CONSTANT_TRANSLATION = tclib.Translation(text='TTTTTT') 30701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 3081eddb32f808ac5930f24b1954b5c949b273a3869joi@chromium.org # A pattern to match messages that are empty or whitespace only. 309eda8d69f7c49419eef6e96a1cd42af26a864d794joi@chromium.org WHITESPACE_MESSAGE = lazy_re.compile(u'^\s*$') 3101eddb32f808ac5930f24b1954b5c949b273a3869joi@chromium.org 31101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def __init__(self, uber_clique, message, translateable=True, custom_type=None): 31201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Create a new clique initialized with just a message. 31301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 3141eddb32f808ac5930f24b1954b5c949b273a3869joi@chromium.org Note that messages with a body comprised only of whitespace will implicitly 3151eddb32f808ac5930f24b1954b5c949b273a3869joi@chromium.org be marked non-translatable. 3161eddb32f808ac5930f24b1954b5c949b273a3869joi@chromium.org 31701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Args: 31801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org uber_clique: Our uber-clique (collection of cliques) 31901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org message: tclib.Message() 32001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org translateable: True | False 32101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org custom_type: instance of clique.CustomType interface 32201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 32301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # Our parent 32401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.uber_clique = uber_clique 32501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # If not translateable, we only store the original message. 32601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.translateable = translateable 3271eddb32f808ac5930f24b1954b5c949b273a3869joi@chromium.org 3281eddb32f808ac5930f24b1954b5c949b273a3869joi@chromium.org # We implicitly mark messages that have a whitespace-only body as 3291eddb32f808ac5930f24b1954b5c949b273a3869joi@chromium.org # non-translateable. 3301eddb32f808ac5930f24b1954b5c949b273a3869joi@chromium.org if MessageClique.WHITESPACE_MESSAGE.match(message.GetRealContent()): 3311eddb32f808ac5930f24b1954b5c949b273a3869joi@chromium.org self.translateable = False 3321eddb32f808ac5930f24b1954b5c949b273a3869joi@chromium.org 33301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # A mapping of language identifiers to tclib.BaseMessage and its 33401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # subclasses (i.e. tclib.Message and tclib.Translation). 33501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.clique = { MessageClique.source_language : message } 33601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # A list of the "shortcut groups" this clique is 33701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # part of. Within any given shortcut group, no shortcut key (e.g. &J) 33801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # must appear more than once in each language for all cliques that 33901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # belong to the group. 34001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.shortcut_groups = [] 34101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # An instance of the CustomType interface, or None. If this is set, it will 34201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # be used to validate the original message and translations thereof, and 34301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # will also get a chance to modify translations of the message. 34401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.SetCustomType(custom_type) 34501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 34601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def GetMessage(self): 34701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Retrieves the tclib.Message that is the source for this clique.''' 34801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return self.clique[MessageClique.source_language] 34901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 35001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def GetId(self): 35101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Retrieves the message ID of the messages in this clique.''' 35201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return self.GetMessage().GetId() 35301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 35401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def IsTranslateable(self): 35501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return self.translateable 35601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 35701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def AddToShortcutGroup(self, group): 35801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.shortcut_groups.append(group) 35901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 36001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def SetCustomType(self, custom_type): 36101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Makes this clique use custom_type for validating messages and 36201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org translations, and optionally modifying translations. 36301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 36401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.custom_type = custom_type 36501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if custom_type and not custom_type.Validate(self.GetMessage()): 36601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org raise exception.InvalidMessage(self.GetMessage().GetRealContent()) 36701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 36801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def MessageForLanguage(self, lang, pseudo_if_no_match=True, fallback_to_english=False): 36901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns the message/translation for the specified language, providing 37001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org a pseudotranslation if there is no available translation and a pseudo- 37101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org translation is requested. 37201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 37301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org The translation of any message whatsoever in the special language 37401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 'x_constant' is the message "TTTTTT". 37501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 37601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Args: 37701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org lang: 'en' 37801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org pseudo_if_no_match: True 37901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org fallback_to_english: False 38001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 38101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Return: 38201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org tclib.BaseMessage 38301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 38401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if not self.translateable: 38501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return self.GetMessage() 38601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 38701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if lang == constants.CONSTANT_LANGUAGE: 38801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return self.CONSTANT_TRANSLATION 38901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 39001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for msglang in self.clique.keys(): 39101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if lang == msglang: 39201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return self.clique[msglang] 39301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 39401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if lang == constants.FAKE_BIDI: 39501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return pseudo_rtl.PseudoRTLMessage(self.GetMessage()) 39601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 39701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if fallback_to_english: 39801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.uber_clique._AddMissingTranslation(lang, self, is_error=False) 39901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return self.GetMessage() 40001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 40101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # If we're not supposed to generate pseudotranslations, we add an error 40201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # report to a list of errors, then fail at a higher level, so that we 40301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # get a list of all messages that are missing translations. 40401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if not pseudo_if_no_match: 40501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.uber_clique._AddMissingTranslation(lang, self, is_error=True) 40601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 40701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return pseudo.PseudoMessage(self.GetMessage()) 40801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 40901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def AllMessagesThatMatch(self, lang_re, include_pseudo = True): 41001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Returns a map of all messages that match 'lang', including the pseudo 41101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org translation if requested. 41201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 41301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Args: 41401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org lang_re: re.compile('fr|en') 41501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org include_pseudo: True 41601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 41701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Return: 41801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org { 'en' : tclib.Message, 41901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 'fr' : tclib.Translation, 42001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org pseudo.PSEUDO_LANG : tclib.Translation } 42101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 42201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if not self.translateable: 42301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return [self.GetMessage()] 42401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 42501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org matches = {} 42601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org for msglang in self.clique: 42701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if lang_re.match(msglang): 42801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org matches[msglang] = self.clique[msglang] 42901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 43001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if include_pseudo: 43101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org matches[pseudo.PSEUDO_LANG] = pseudo.PseudoMessage(self.GetMessage()) 43201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 43301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return matches 43401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 43501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def AddTranslation(self, translation, language): 43601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Add a translation to this clique. The translation must have the same 43701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ID as the message that is the source for this clique. 43801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 43901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org If this clique is not translateable, the function just returns. 44001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 44101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Args: 44201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org translation: tclib.Translation() 44301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org language: 'en' 44401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 44501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Throws: 44601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org grit.exception.InvalidTranslation if the translation you're trying to add 44701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org doesn't have the same message ID as the source message of this clique. 44801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 44901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if not self.translateable: 45001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return 45101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if translation.GetId() != self.GetId(): 45201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org raise exception.InvalidTranslation( 45301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 'Msg ID %s, transl ID %s' % (self.GetId(), translation.GetId())) 45401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 45501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org assert not language in self.clique 45601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 45701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # Because two messages can differ in the original content of their 45801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # placeholders yet share the same ID (because they are otherwise the 45901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # same), the translation we are getting may have different original 46001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # content for placeholders than our message, yet it is still the right 46101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # translation for our message (because it is for the same ID). We must 46201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # therefore fetch the original content of placeholders from our original 46301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # English message. 46401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # 46501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # See grit.clique_unittest.MessageCliqueUnittest.testSemiIdenticalCliques 46601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # for a concrete explanation of why this is necessary. 46701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 46801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org original = self.MessageForLanguage(self.source_language, False) 46901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if len(original.GetPlaceholders()) != len(translation.GetPlaceholders()): 47001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org print ("ERROR: '%s' translation of message id %s does not match" % 47101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org (language, translation.GetId())) 47201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org assert False 47301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 47401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org transl_msg = tclib.Translation(id=self.GetId(), 47501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org text=translation.GetPresentableContent(), 47601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org placeholders=original.GetPlaceholders()) 47701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 47801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if self.custom_type and not self.custom_type.ValidateAndModify(language, transl_msg): 47901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org print "WARNING: %s translation failed validation: %s" % ( 48001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org language, transl_msg.GetId()) 48101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 48201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.clique[language] = transl_msg 48301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 484