def testSemiIdenticalCliques(self): messages = [ tclib.Message( text='Hello USERNAME', placeholders=[tclib.Placeholder('USERNAME', '$1', 'Joi')]), tclib.Message( text='Hello USERNAME', placeholders=[tclib.Placeholder('USERNAME', '%s', 'Joi')]), ] self.failUnless(messages[0].GetId() == messages[1].GetId()) # Both of the above would share a translation. translation = tclib.Translation( id=messages[0].GetId(), text='Bonjour USERNAME', placeholders=[tclib.Placeholder('USERNAME', '$1', 'Joi')]) factory = clique.UberClique() cliques = [factory.MakeClique(msg) for msg in messages] for clq in cliques: clq.AddTranslation(translation, 'fr') self.failUnless(cliques[0].MessageForLanguage('fr').GetRealContent() == 'Bonjour $1') self.failUnless(cliques[1].MessageForLanguage('fr').GetRealContent() == 'Bonjour %s')
def Callback(id, structure): if id not in self.cliques_: if debug: print "Ignoring translation #%s" % id return if debug: print "Adding translation #%s" % id # We fetch placeholder information from the original message (the XTB file # only contains placeholder names). original_msg = self.BestClique(id).GetMessage() translation = tclib.Translation(id=id) for is_ph,text in structure: if not is_ph: translation.AppendText(text) else: found_placeholder = False for ph in original_msg.GetPlaceholders(): if ph.GetPresentation() == text: translation.AppendPlaceholder(tclib.Placeholder( ph.GetPresentation(), ph.GetOriginal(), ph.GetExample())) found_placeholder = True break if not found_placeholder: raise exception.MismatchingPlaceholders( 'Translation for message ID %s had <ph name="%s%/>, no match\n' 'in original message' % (id, text)) self.FindCliqueAndAddTranslation(translation, lang)
def testValidate(self): factory = clique.UberClique() msg = tclib.Message(text='Bingo bongo') c = factory.MakeClique(msg) c.SetCustomType(filename.WindowsFilename()) translation = tclib.Translation(id=msg.GetId(), text='Bilingo bolongo:') c.AddTranslation(translation, 'fr') self.failUnless(c.MessageForLanguage('fr').GetRealContent() == 'Bilingo bolongo ')
def testAll(self): text = u'Howdie USERNAME' phs = [tclib.Placeholder(u'USERNAME', u'%s', 'Joi')] msg = tclib.Message(text=text, placeholders=phs) self.failUnless(msg.GetPresentableContent() == 'Howdie USERNAME') trans = tclib.Translation(text=text, placeholders=phs) self.failUnless(trans.GetPresentableContent() == 'Howdie USERNAME') self.failUnless( isinstance(trans.GetPresentableContent(), types.StringTypes))
def testClique(self): factory = clique.UberClique() msg = tclib.Message( text='Hello USERNAME, how are you?', placeholders=[tclib.Placeholder('USERNAME', '%s', 'Joi')]) c = factory.MakeClique(msg) self.failUnless(c.GetMessage() == msg) self.failUnless(c.GetId() == msg.GetId()) msg_fr = tclib.Translation( text='Bonjour USERNAME, comment ca va?', id=msg.GetId(), placeholders=[tclib.Placeholder('USERNAME', '%s', 'Joi')]) msg_de = tclib.Translation( text='Guten tag USERNAME, wie geht es dir?', id=msg.GetId(), placeholders=[tclib.Placeholder('USERNAME', '%s', 'Joi')]) c.AddTranslation(msg_fr, 'fr') factory.FindCliqueAndAddTranslation(msg_de, 'de') # sort() sorts lists in-place and does not return them for lang in ('en', 'fr', 'de'): self.failUnless(lang in c.clique) self.failUnless( c.MessageForLanguage('fr').GetRealContent() == msg_fr.GetRealContent()) try: c.MessageForLanguage('zh-CN', False) self.fail('Should have gotten exception') except: pass self.failUnless(c.MessageForLanguage('zh-CN', True) != None) rex = re.compile('fr|de|bingo') self.failUnless(len(c.AllMessagesThatMatch(rex, False)) == 2) self.failUnless( c.AllMessagesThatMatch(rex, True)[pseudo.PSEUDO_LANG] is not None)
def AddTranslation(self, translation, language): '''Add a translation to this clique. The translation must have the same ID as the message that is the source for this clique. If this clique is not translateable, the function just returns. Args: translation: tclib.Translation() language: 'en' Throws: grit.exception.InvalidTranslation if the translation you're trying to add doesn't have the same message ID as the source message of this clique. ''' if not self.translateable: return if translation.GetId() != self.GetId(): raise exception.InvalidTranslation( 'Msg ID %s, transl ID %s' % (self.GetId(), translation.GetId())) if language in self.clique: print(self.GetId()) assert not language in self.clique # Because two messages can differ in the original content of their # placeholders yet share the same ID (because they are otherwise the # same), the translation we are getting may have different original # content for placeholders than our message, yet it is still the right # translation for our message (because it is for the same ID). We must # therefore fetch the original content of placeholders from our original # English message. # # See grit.clique_unittest.MessageCliqueUnittest.testSemiIdenticalCliques # for a concrete explanation of why this is necessary. original = self.MessageForLanguage(self.source_language, False) if len(original.GetPlaceholders()) != len( translation.GetPlaceholders()): print("ERROR: '%s' translation of message id %s does not match" % (language, translation.GetId())) assert False transl_msg = tclib.Translation( id=self.GetId(), text=translation.GetPresentableContent(), placeholders=original.GetPlaceholders()) if (self.custom_type and not self.custom_type.ValidateAndModify(language, transl_msg)): print "WARNING: %s translation failed validation: %s" % ( language, transl_msg.GetId()) self.clique[language] = transl_msg
def testRegressionTranslationInherited(self): '''Regression tests a bug that was caused by grit.tclib.Translation inheriting from the translation console's Translation object instead of only owning an instance of it. ''' msg = tclib.Message(text=u"BLA1\r\nFrom: BLA2 \u00fe BLA3", placeholders=[ tclib.Placeholder('BLA1', '%s', '%s'), tclib.Placeholder('BLA2', '%s', '%s'), tclib.Placeholder('BLA3', '%s', '%s') ]) transl = tclib.Translation(text=msg.GetPresentableContent(), placeholders=msg.GetPlaceholders()) content = transl.GetContent() self.failUnless(isinstance(content[3], types.UnicodeType))
def update_translation(options, resources, translations, entry): comments = [x.strip() for x in entry.comment.splitlines()] arguments = dict([x.split(":", 1) for x in comments if ":" in x]) if VIVALDI_FILE not in arguments: return if arguments[VIVALDI_FILE].strip().split(".")[-1] != options.vivaldi_file: return if TRANSLATIONID not in arguments: return translation = entry.msgstr tid = arguments[TRANSLATIONID].strip() desc = arguments.get(DESCRIPTION, "").strip() if tid in handled_translations: return handled_translations.add(tid) if tid in translations: if "translation" in translations[tid]: translations[tid]["translation"].parts = [translation] translations[tid]["translation"].placeholders = [] else: if "node" in translations[tid]: desc = translations[tid]["node"].attrs.get("desc", desc) item = tclib.Translation(id=tid, text=translation, description=desc) translations[tid]["translations"] = item else: item = tclib.Translation(id=tid, text=translation, description=desc) translations.setdefault(tid, {})["translations"] = item
def ToTranslation(tree, placeholders): """Converts the tree back to a translation, substituting the placeholders back in as required. """ text = tree.ToString() assert text.count(PLACEHOLDER_STRING) == len(placeholders) transl = tclib.Translation() for placeholder in placeholders: index = text.find(PLACEHOLDER_STRING) if index > 0: transl.AppendText(text[:index]) text = text[index + len(PLACEHOLDER_STRING):] transl.AppendPlaceholder(placeholder) if text: transl.AppendText(text) return transl
def PseudoRTLMessage(message): '''Returns a pseudo-RTL (aka Fake-Bidi) translation of the provided message. Args: message: tclib.Message() Return: tclib.Translation() ''' transl = tclib.Translation() for part in message.GetContent(): if isinstance(part, tclib.Placeholder): transl.AppendPlaceholder(part) else: transl.AppendText(PseudoRTLString(part)) return transl
def testCustomTypes(self): factory = clique.UberClique() message = tclib.Message(text='Bingo bongo') c = factory.MakeClique(message) try: c.SetCustomType(DummyCustomType()) self.fail() except: pass # expected case - 'Bingo bongo' does not start with 'jjj' message = tclib.Message(text='jjjBingo bongo') c = factory.MakeClique(message) c.SetCustomType(util.NewClassInstance( 'grit.clique_unittest.DummyCustomType', clique.CustomType)) translation = tclib.Translation(id=message.GetId(), text='Bilingo bolongo') c.AddTranslation(translation, 'fr') self.failUnless(c.MessageForLanguage('fr').GetRealContent().startswith('jjj'))
class MessageClique(object): '''A message along with all of its translations. Also code to bring translations together with their original message.''' # change this to the language code of Messages you add to cliques_. # TODO(joi) Actually change this based on the <grit> node's source language source_language = 'en' # A constant translation we use when asked for a translation into the # special language constants.CONSTANT_LANGUAGE. CONSTANT_TRANSLATION = tclib.Translation(text='TTTTTT') def __init__(self, uber_clique, message, translateable=True, custom_type=None): '''Create a new clique initialized with just a message. Args: uber_clique: Our uber-clique (collection of cliques) message: tclib.Message() translateable: True | False custom_type: instance of clique.CustomType interface ''' # Our parent self.uber_clique = uber_clique # If not translateable, we only store the original message. self.translateable = translateable # A mapping of language identifiers to tclib.BaseMessage and its # subclasses (i.e. tclib.Message and tclib.Translation). self.clique = { MessageClique.source_language : message } # A list of the "shortcut groups" this clique is # part of. Within any given shortcut group, no shortcut key (e.g. &J) # must appear more than once in each language for all cliques that # belong to the group. self.shortcut_groups = [] # An instance of the CustomType interface, or None. If this is set, it will # be used to validate the original message and translations thereof, and # will also get a chance to modify translations of the message. self.SetCustomType(custom_type) def GetMessage(self): '''Retrieves the tclib.Message that is the source for this clique.''' return self.clique[MessageClique.source_language] def GetId(self): '''Retrieves the message ID of the messages in this clique.''' return self.GetMessage().GetId() def IsTranslateable(self): return self.translateable def AddToShortcutGroup(self, group): self.shortcut_groups.append(group) def SetCustomType(self, custom_type): '''Makes this clique use custom_type for validating messages and translations, and optionally modifying translations. ''' self.custom_type = custom_type if custom_type and not custom_type.Validate(self.GetMessage()): raise exception.InvalidMessage(self.GetMessage().GetRealContent()) def MessageForLanguage(self, lang, pseudo_if_no_match=True, fallback_to_english=False): '''Returns the message/translation for the specified language, providing a pseudotranslation if there is no available translation and a pseudo- translation is requested. The translation of any message whatsoever in the special language 'x_constant' is the message "TTTTTT". Args: lang: 'en' pseudo_if_no_match: True fallback_to_english: False Return: tclib.BaseMessage ''' if not self.translateable: return self.GetMessage() if lang == constants.CONSTANT_LANGUAGE: return self.CONSTANT_TRANSLATION for msglang in self.clique.keys(): if lang == msglang: return self.clique[msglang] if lang == constants.FAKE_BIDI: return pseudo_rtl.PseudoRTLMessage(self.GetMessage()) if fallback_to_english: self.uber_clique._AddMissingTranslation(lang, self, is_error=False) return self.GetMessage() # If we're not supposed to generate pseudotranslations, we add an error # report to a list of errors, then fail at a higher level, so that we # get a list of all messages that are missing translations. if not pseudo_if_no_match: self.uber_clique._AddMissingTranslation(lang, self, is_error=True) return pseudo.PseudoMessage(self.GetMessage()) def AllMessagesThatMatch(self, lang_re, include_pseudo = True): '''Returns a map of all messages that match 'lang', including the pseudo translation if requested. Args: lang_re: re.compile('fr|en') include_pseudo: True Return: { 'en' : tclib.Message, 'fr' : tclib.Translation, pseudo.PSEUDO_LANG : tclib.Translation } ''' if not self.translateable: return [self.GetMessage()] matches = {} for msglang in self.clique: if lang_re.match(msglang): matches[msglang] = self.clique[msglang] if include_pseudo: matches[pseudo.PSEUDO_LANG] = pseudo.PseudoMessage(self.GetMessage()) return matches def AddTranslation(self, translation, language): '''Add a translation to this clique. The translation must have the same ID as the message that is the source for this clique. If this clique is not translateable, the function just returns. Args: translation: tclib.Translation() language: 'en' Throws: grit.exception.InvalidTranslation if the translation you're trying to add doesn't have the same message ID as the source message of this clique. ''' if not self.translateable: return if translation.GetId() != self.GetId(): raise exception.InvalidTranslation( 'Msg ID %s, transl ID %s' % (self.GetId(), translation.GetId())) assert not language in self.clique # Because two messages can differ in the original content of their # placeholders yet share the same ID (because they are otherwise the # same), the translation we are getting may have different original # content for placeholders than our message, yet it is still the right # translation for our message (because it is for the same ID). We must # therefore fetch the original content of placeholders from our original # English message. # # See grit.clique_unittest.MessageCliqueUnittest.testSemiIdenticalCliques # for a concrete explanation of why this is necessary. original = self.MessageForLanguage(self.source_language, False) if len(original.GetPlaceholders()) != len(translation.GetPlaceholders()): print ("ERROR: '%s' translation of message id %s does not match" % (language, translation.GetId())) assert False transl_msg = tclib.Translation(id=self.GetId(), text=translation.GetPresentableContent(), placeholders=original.GetPlaceholders()) if self.custom_type and not self.custom_type.ValidateAndModify(language, transl_msg): print "WARNING: %s translation failed validation: %s" % ( language, transl_msg.GetId()) self.clique[language] = transl_msg