# 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 tclib module contains tools for aggregating, verifying, and storing # messages destined for the Translation Console, as well as for reading # translations back and outputting them in some desired format. # # This has been stripped down to include only the functionality needed by grit # for creating Windows .rc and .h files. These are the only parts needed by # the Chrome build process.
from __future__ import print_function
from grit.extern import FP
# This module assumes that within a bundle no two messages can have the # same id unless they're identical.
# The basic classes defined here for external use are Message and Translation, # where the former is used for English messages and the latter for # translations. These classes have a lot of common functionality, as expressed # by the common parent class BaseMessage. Perhaps the most important # distinction is that translated text is stored in UTF-8, whereas original text # is stored in whatever encoding the client uses (presumably Latin-1).
# -------------------- # The public interface # --------------------
# Generate message id from message text and meaning string (optional), # both in utf-8 encoding # def GenerateMessageId(message, meaning=''):
fp = FP.FingerPrint(message) if meaning: # combine the fingerprints of message and meaning
fp2 = FP.FingerPrint(meaning) if fp < 0:
fp = fp2 + (fp << 1) + 1 else:
fp = fp2 + (fp << 1) # To avoid negative ids we strip the high-order bit return str(fp & 0x7fffffffffffffff)
# ------------------------------------------------------------------------- # The MessageTranslationError class is used to signal tclib-specific errors.
class MessageTranslationError(Exception):
def __init__(self, args = ''):
self.args = args
# ----------------------------------------------------------- # The Placeholder class represents a placeholder in a message.
# Equality test # # ignore_trailing_spaces: TC is using varchar to store the # phrwr fields, as a result of that, the trailing spaces # are removed by MySQL when the strings are stored into TC:-( # ignore_trailing_spaces parameter is used to ignore # trailing spaces during equivalence comparison. # def EqualTo(self, other, strict = 1, ignore_trailing_spaces = 1): if type(other) isnot Placeholder: return 0 if StringEquals(self.__presentation, other.__presentation,
ignore_trailing_spaces): ifnot strict or (StringEquals(self.__original, other.__original,
ignore_trailing_spaces) and
StringEquals(self.__example, other.__example,
ignore_trailing_spaces)): return 1 return 0
# ----------------------------------------------------------------- # BaseMessage is the common parent class of Message and Translation. # It is not meant for direct use.
class BaseMessage(object): # Three types of message construction is supported. If the message text is a # simple string with no dynamic content, you can pass it to the constructor # as the "text" parameter. Otherwise, you can omit "text" and assemble the # message step by step using AppendText() and AppendPlaceholder(). Or, as an # alternative, you can give the constructor the "presentable" version of the # message and a list of placeholders; it will then parse the presentation and # build the message accordingly. For example: # Message(text = "There are NUM_BUGS bugs in your code", # placeholders = [Placeholder("NUM_BUGS", "%d", "33")], # description = "Bla bla bla") def __eq__(self, other): # "source encoding" is nonsense, so ignore it return _ObjectEquals(self, other, ['_BaseMessage__source_encoding'])
# Append a placeholder to the message def AppendPlaceholder(self, placeholder): ifnot isinstance(placeholder, Placeholder): raise MessageTranslationError("Invalid message placeholder %s in " "message %s" % (placeholder, self.GetId())) # Are there other placeholders with the same presentation? # If so, they need to be the same. for other in self.GetPlaceholders(): if placeholder.GetPresentation() == other.GetPresentation(): ifnot placeholder.EqualTo(other): raise MessageTranslationError( "Conflicting declarations of %s within message" %
placeholder.GetPresentation()) # update placeholder list
dup = 0 for item in self.__content: if isinstance(item, Placeholder) and placeholder.EqualTo(item):
dup = 1 break ifnot dup:
self.__placeholders.append(placeholder)
# Strips leading and trailing whitespace, and returns a tuple # containing the leading and trailing space that was removed. def Strip(self):
leading = trailing = '' if len(self.__content) > 0:
s0 = self.__content[0] ifnot isinstance(s0, Placeholder):
s = s0.lstrip()
leading = s0[:-len(s)]
self.__content[0] = s
s0 = self.__content[-1] ifnot isinstance(s0, Placeholder):
s = s0.rstrip()
trailing = s0[len(s):]
self.__content[-1] = s return leading, trailing
# Return the id of this message def GetId(self): if self.__id isNone: return self.GenerateId() return self.__id
# Set the id of this message def SetId(self, id): if id isNone:
self.__id = None else:
self.__id = str(id) # Treat numerical ids as strings
# Return content of this message as a list (internal use only) def GetContent(self): return self.__content
# Return a human-readable version of this message def GetPresentableContent(self):
presentable_content = "" for item in self.__content: if isinstance(item, Placeholder):
presentable_content += item.GetPresentation() else:
presentable_content += item
return presentable_content
# Return a fragment of a message in escaped format def EscapeFragment(self, fragment): return fragment.replace('%', '%%')
# Return the "original" version of this message, doing %-escaping # properly. If source_msg is specified, the placeholder original # information inside source_msg will be used instead. def GetOriginalContent(self, source_msg = None):
original_content = "" for item in self.__content: if isinstance(item, Placeholder): if source_msg:
ph = source_msg.GetPlaceholder(item.GetPresentation()) ifnot ph: raise MessageTranslationError( "Placeholder %s doesn't exist in message: %s" %
(item.GetPresentation(), source_msg))
original_content += ph.GetOriginal() else:
original_content += item.GetOriginal() else:
original_content += self.EscapeFragment(item) return original_content
# Return the example of this message def GetExampleContent(self):
example_content = "" for item in self.__content: if isinstance(item, Placeholder):
example_content += item.GetExample() else:
example_content += item return example_content
# Return a list of all unique placeholders in this message def GetPlaceholders(self): return self.__placeholders
# Return a placeholder in this message def GetPlaceholder(self, presentation): for item in self.__content: if (isinstance(item, Placeholder) and
item.GetPresentation() == presentation): return item returnNone
# Return this message's description def GetDescription(self): return self.__description
# Add a message source def AddSource(self, source):
self.__sources.append(source)
# Return this message's sources as a list def GetSources(self): return self.__sources
# Return this message's sources as a string def GetSourcesAsText(self, delimiter = "; "): return delimiter.join(self.__sources)
# Set the obsolete flag for a message (internal use only) def SetObsolete(self):
self.__obsolete = 1
# Get the obsolete flag for a message (internal use only) def IsObsolete(self): return self.__obsolete
# Get the sequence number (0 by default) def GetSequenceNumber(self): return self.__sequence_number
# Set the sequence number def SetSequenceNumber(self, number):
self.__sequence_number = number
def GetErrors(self, from_tc=0): """
Returns a description of the problem if the message isnot
syntactically valid, orNoneif everything is fine.
Args:
from_tc: indicates whether this message came from the TC. We let
the TC get away with some things we normally wouldn't allow for
historical reasons. """ # check that placeholders are unambiguous
pos = 0
phs = {} for item in self.__content: if isinstance(item, Placeholder):
phs[pos] = item
pos += len(item.GetPresentation()) else:
pos += len(item)
presentation = self.GetPresentableContent() for ph in self.GetPlaceholders(): for pos in FindOverlapping(presentation, ph.GetPresentation()): # message contains the same text as a placeholder presentation
other_ph = phs.get(pos) if ((not other_ph andnot IsSubstringInPlaceholder(pos, len(ph.GetPresentation()), phs)) or
(other_ph and len(other_ph.GetPresentation()) < len(ph.GetPresentation()))): return"message contains placeholder name '%s':\n%s" % (
ph.GetPresentation(), presentation) returnNone
def HasText(self): """Returns true iff this message has anything other than placeholders.""" for item in self.__content: ifnot isinstance(item, Placeholder): returnTrue returnFalse
# -------------------------------------------------------- # The Message class represents original (English) messages
# String representation def __str__(self):
s = 'source: %s, id: %s, content: "%s", meaning: "%s", ' \ 'description: "%s"' % \
(self.GetSourcesAsText(), self.GetId(), self.GetPresentableContent(),
self.__meaning, self.GetDescription()) if self.GetName() isnotNone:
s += ', name: "%s"' % self.GetName()
placeholders = self.GetPlaceholders() for i in range(len(placeholders)):
s += ", placeholder[%d]: %s" % (i, placeholders[i]) return s
# Strips leading and trailing whitespace, and returns a tuple # containing the leading and trailing space that was removed. def Strip(self):
leading = trailing = ''
content = self.GetContent() if len(content) > 0:
s0 = content[0] ifnot isinstance(s0, Placeholder):
s = s0.lstrip()
leading = s0[:-len(s)]
content[0] = s
s0 = content[-1] ifnot isinstance(s0, Placeholder):
s = s0.rstrip()
trailing = s0[len(s):]
content[-1] = s return leading, trailing
# Generate an id by hashing message content def GenerateId(self):
self.SetId(GenerateMessageId(self.GetPresentableContent(),
self.__meaning)) return self.GetId()
# Equality operator def EqualTo(self, other, strict = 1): # Check id, meaning, content if self.GetId() != other.GetId(): return 0 if self.__meaning != other.__meaning: return 0 if self.GetPresentableContent() != other.GetPresentableContent(): return 0 # Check descriptions if comparison is strict if (strict and
self.GetDescription() isnotNoneand
other.GetDescription() isnotNoneand
self.GetDescription() != other.GetDescription()): return 0 # Check placeholders
ph1 = self.GetPlaceholders()
ph2 = other.GetPlaceholders() if len(ph1) != len(ph2): return 0 for i in range(len(ph1)): ifnot ph1[i].EqualTo(ph2[i], strict): return 0
return 1
def Copy(self): """
Returns a copy of this Message. """ assert isinstance(self, Message) return Message(None, clone_from=self)
def SetIsHidden(self, is_hidden): """Sets whether this message should be hidden.
Args:
is_hidden : 0 or 1 - if the message should be hidden, 0 otherwise """ if is_hidden notin [0, 1]: raise MessageTranslationError("is_hidden must be 0 or 1, got %s")
self.__is_hidden = is_hidden
def IsHidden(self): """Returns 1 if this message is hidden, and 0 otherwise.""" return self.__is_hidden
# ---------------------------------------------------- # The Translation class represents translated messages
class Translation(BaseMessage): # See BaseMessage constructor def __init__(self, source_encoding, text=None, id=None,
description=None, placeholders=None, source=None,
sequence_number=0, clone_from=None, ignore_ph_errors=0,
name=None): if clone_from isnotNone:
BaseMessage.__init__(self, None, clone_from=clone_from) return
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.