Home | History | Annotate | Download | only in gather
      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 '''A baseclass for simple gatherers that store their gathered resource in a
      7 list.
      8 '''
      9 
     10 import types
     11 
     12 from grit.gather import interface
     13 from grit import clique
     14 from grit import tclib
     15 
     16 
     17 class SkeletonGatherer(interface.GathererBase):
     18   '''Common functionality of gatherers that parse their input as a skeleton of
     19   translatable and nontranslatable chunks.
     20   '''
     21 
     22   def __init__(self, *args, **kwargs):
     23     super(SkeletonGatherer, self).__init__(*args, **kwargs)
     24     # List of parts of the document. Translateable parts are
     25     # clique.MessageClique objects, nontranslateable parts are plain strings.
     26     # Translated messages are inserted back into the skeleton using the quoting
     27     # rules defined by self.Escape()
     28     self.skeleton_ = []
     29     # A list of the names of IDs that need to be defined for this resource
     30     # section to compile correctly.
     31     self.ids_ = []
     32     # True if Parse() has already been called.
     33     self.have_parsed_ = False
     34     # True if a translatable chunk has been added
     35     self.translatable_chunk_ = False
     36     # If not None, all parts of the document will be put into this single
     37     # message; otherwise the normal skeleton approach is used.
     38     self.single_message_ = None
     39     # Number to use for the next placeholder name.  Used only if single_message
     40     # is not None
     41     self.ph_counter_ = 1
     42 
     43   def GetText(self):
     44     '''Returns the original text of the section'''
     45     return self.text_
     46 
     47   def Escape(self, text):
     48     '''Subclasses can override.  Base impl is identity.
     49     '''
     50     return text
     51 
     52   def UnEscape(self, text):
     53     '''Subclasses can override. Base impl is identity.
     54     '''
     55     return text
     56 
     57   def GetTextualIds(self):
     58     '''Returns the list of textual IDs that need to be defined for this
     59     resource section to compile correctly.'''
     60     return self.ids_
     61 
     62   def _AddTextualId(self, id):
     63     self.ids_.append(id)
     64 
     65   def GetCliques(self):
     66     '''Returns the message cliques for each translateable message in the
     67     resource section.'''
     68     return [x for x in self.skeleton_ if isinstance(x, clique.MessageClique)]
     69 
     70   def Translate(self, lang, pseudo_if_not_available=True,
     71                 skeleton_gatherer=None, fallback_to_english=False):
     72     if len(self.skeleton_) == 0:
     73       raise exception.NotReady()
     74     if skeleton_gatherer:
     75       assert len(skeleton_gatherer.skeleton_) == len(self.skeleton_)
     76 
     77     out = []
     78     for ix in range(len(self.skeleton_)):
     79       if isinstance(self.skeleton_[ix], types.StringTypes):
     80         if skeleton_gatherer:
     81           # Make sure the skeleton is like the original
     82           assert(isinstance(skeleton_gatherer.skeleton_[ix], types.StringTypes))
     83           out.append(skeleton_gatherer.skeleton_[ix])
     84         else:
     85           out.append(self.skeleton_[ix])
     86       else:
     87         if skeleton_gatherer:  # Make sure the skeleton is like the original
     88           assert(not isinstance(skeleton_gatherer.skeleton_[ix],
     89                                 types.StringTypes))
     90         msg = self.skeleton_[ix].MessageForLanguage(lang,
     91                                                     pseudo_if_not_available,
     92                                                     fallback_to_english)
     93 
     94         def MyEscape(text):
     95           return self.Escape(text)
     96         text = msg.GetRealContent(escaping_function=MyEscape)
     97         out.append(text)
     98     return ''.join(out)
     99 
    100   def Parse(self):
    101     '''Parses the section.  Implemented by subclasses.  Idempotent.'''
    102     raise NotImplementedError()
    103 
    104   def _AddNontranslateableChunk(self, chunk):
    105     '''Adds a nontranslateable chunk.'''
    106     if self.single_message_:
    107       ph = tclib.Placeholder('XX%02dXX' % self.ph_counter_, chunk, chunk)
    108       self.ph_counter_ += 1
    109       self.single_message_.AppendPlaceholder(ph)
    110     else:
    111       self.skeleton_.append(chunk)
    112 
    113   def _AddTranslateableChunk(self, chunk):
    114     '''Adds a translateable chunk.  It will be unescaped before being added.'''
    115     # We don't want empty messages since they are redundant and the TC
    116     # doesn't allow them.
    117     if chunk == '':
    118       return
    119 
    120     unescaped_text = self.UnEscape(chunk)
    121     if self.single_message_:
    122       self.single_message_.AppendText(unescaped_text)
    123     else:
    124       self.skeleton_.append(self.uberclique.MakeClique(
    125         tclib.Message(text=unescaped_text)))
    126       self.translatable_chunk_ = True
    127 
    128   def SubstituteMessages(self, substituter):
    129     '''Applies substitutions to all messages in the tree.
    130 
    131     Goes through the skeleton and finds all MessageCliques.
    132 
    133     Args:
    134       substituter: a grit.util.Substituter object.
    135     '''
    136     if self.single_message_:
    137       self.single_message_ = substituter.SubstituteMessage(self.single_message_)
    138     new_skel = []
    139     for chunk in self.skeleton_:
    140       if isinstance(chunk, clique.MessageClique):
    141         old_message = chunk.GetMessage()
    142         new_message = substituter.SubstituteMessage(old_message)
    143         if new_message is not old_message:
    144           new_skel.append(self.uberclique.MakeClique(new_message))
    145           continue
    146       new_skel.append(chunk)
    147     self.skeleton_ = new_skel
    148