Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  tr_html.py   Sprache: Python

 
# 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.

'''A gatherer for the TotalRecall brand of HTML templates with replaceable
portions.  We wanted to reuse extern.tclib.api.handlers.html.TCHTMLParser
but this proved impossible due to the fact that the TotalRecall HTML templates
are in general quite far from parseable HTML and the TCHTMLParser derives

from HTMLParser.HTMLParser which requires relatively well-formed HTML.  Some
examples of "HTML" from the TotalRecall HTML templates that wouldn't be
parseable include things like:

  <a [PARAMS]>blabla</a>  (not parseable because attributes are invalid)

  <table><tr><td>[LOTSOFSTUFF]</tr></table> (not parseable because closing
                                            </td> is in the HTML [LOTSOFSTUFF]
                                            is replaced by)

The other problem with using general parsers (such as TCHTMLParser) is that
we want to make sure we output the TotalRecall template with as little changes
as possible in terms of whitespace characters, layout etc.  With any parser
that generates a parse tree, and generates output by dumping the parse tree,
we would always have little inconsistencies which could cause bugs (the
TotalRecall template stuff is quite brittle and can break if e.g. a tab
character is replaced with spaces).

The solution, which may be applicable to some other HTML-like template
languages floating around Google, is to create a parser with a simple state
machine that keeps track of what kind of tag it's inside, and whether it'in
a translateable section or not.  Translateable sections are:

a) text (including [BINGO] replaceables) inside of tags that
   can contain translateable text (which is all tags except
   for a few)

b) text inside of an 'alt' attribute in an <image> element, or
   the 'value' attribute of a <submit>, <button> or <text>
   element.

The parser does not build up a parse tree but rather a "skeleton" which
is a list of nontranslateable strings intermingled with grit.clique.MessageClique
objects.  This simplifies the parser considerably compared to a regular HTML
parser.  To output a translated document, each item in the skeleton is
printed out, with the relevant Translation from each MessageCliques being used
for the requested language.

This implementation borrows some code, constants and ideas from
extern.tclib.api.handlers.html.TCHTMLParser.
'''

from __future__ import print_function

import re

import six

from grit import clique
from grit import exception
from grit import lazy_re
from grit import util
from grit import tclib

from grit.gather import interface


# HTML tags which break (separate) chunks.
_BLOCK_TAGS = ['script''p''h1''h2''h3''h4''h5''h6''hr''br',
              'body''style''head''title''table''tr''td''th',
              'ul''ol''dl''nl''li''div''object''center',
              'html''link''form''select''textarea',
              'button''option''map''area''blockquote''pre',
              'meta''xmp''noscript''label''tbody''thead',
              'script''style''pre''iframe''img''input''nowrap',
              'fieldset''legend']

# HTML tags which may appear within a chunk.
_INLINE_TAGS = ['b''i''u''tt''code''font''a''span''small',
               'key''nobr''url''em''s''sup''strike',
               'strong']

# HTML tags within which linebreaks are significant.
_PREFORMATTED_TAGS = ['textarea''xmp''pre']

# An array mapping some of the inline HTML tags to more meaningful
# names for those tags.  This will be used when generating placeholders
# representing these tags.
_HTML_PLACEHOLDER_NAMES = { 'a' : 'link''br' : 'break''b' : 'bold',
  'i' : 'italic''li' : 'item''ol' : 'ordered_list''p' : 'paragraph',
  'ul' : 'unordered_list''img' : 'image''em' : 'emphasis' }

# We append each of these characters in sequence to distinguish between
# different placeholders with basically the same name (e.g. BOLD1, BOLD2).
# Keep in mind that a placeholder name must not be a substring of any other
# placeholder name in the same message, so we can't simply count (BOLD_1
# would be a substring of BOLD_10).
_SUFFIXES = '123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'

# Matches whitespace in an HTML document.  Also matches HTML comments, which are
# treated as whitespace.
_WHITESPACE = lazy_re.compile(r'(\s| |\\n|\\r|)+',
                              re.DOTALL)

# Matches whitespace sequences which can be folded into a single whitespace
# character.  This matches single characters so that non-spaces are replaced
# with spaces.
_FOLD_WHITESPACE = lazy_re.compile(r'\s+')

# Finds a non-whitespace character
_NON_WHITESPACE = lazy_re.compile(r'\S')

# Matches two or more   in a row (a single   is not changed into
# placeholders because different languages require different numbers of spaces
# and placeholders must match exactly; more than one is probably a "special"
# whitespace sequence and should be turned into a placeholder).
_NBSP = lazy_re.compile(r' ( )+')

# Matches nontranslateable chunks of the document
_NONTRANSLATEABLES = lazy_re.compile(r'''
  <\s*script.+?<\s*/\s*script\s*>
  |
  <\s*style.+?<\s*/\s*style\s*>
  |
  <!--.+?-->
  |
  <\?IMPORT\s.+?>           # import tag
  |
  <\s*[a-zA-Z_]+:.+?>       # custom tag (open)
  |
  <\s*/\s*[a-zA-Z_]+:.+?>   # custom tag (close)
  |
  <!\s*[A-Z]+\s*([^>]+|"[^"]+"|'[^']+')*?>
  ''', re.MULTILINE | re.DOTALL | re.VERBOSE | re.IGNORECASE)

# Matches a tag and its attributes
_ELEMENT = lazy_re.compile(r'''
  # Optional closing /, element name
  <\s*(?P<closing>/)?\s*(?P<element>[a-zA-Z0-9]+)\s*
  # Attributes and/or replaceables inside the tag, if any
  (?P<atts>(
    \s*([a-zA-Z_][-:.a-zA-Z_0-9]*) # Attribute name
    (\s*=\s*(\'[^\']*\'|"[^"]*"|[-a-zA-Z0-9./,:;+*%?!&$\(\)_#=~\'"@]*))?
    |
    \s*\[(\$?\~)?([A-Z0-9-_]+?)(\~\$?)?\]
  )*)
  \s*(?P<empty>/)?\s*> # Optional empty-tag closing /, and tag close
  ''',
  re.MULTILINE | re.DOTALL | re.VERBOSE)

# Matches elements that may have translateable attributes.  The value of these
# special attributes is given by group 'value1' or 'value2'.  Note that this
# regexp demands that the attribute value be quoted; this is necessary because
# the non-tree-building nature of the parser means we don't know when we're
# writing out attributes, so we wouldn't know to escape spaces.
_SPECIAL_ELEMENT = lazy_re.compile(r'''
  <\s*(
    input[^>]+?value\s*=\s*(\'(?P[^\']*)\'|"(?P[^"]*)")
    [^>]+type\s*=\s*"?'?(button|reset|text|submit)'?"?
    |
    (
      table[^>]+?title\s*=
      |
      img[^>]+?alt\s*=
      |
      input[^>]+?type\s*=\s*"?'?(button|reset|text|submit)'?"?[^>]+?value\s*=
    )
    \s*(\'(?P[^\']*)\'|"(?P[^"]*)")
  )[^>]*?>
  ''', re.MULTILINE | re.DOTALL | re.VERBOSE | re.IGNORECASE)

# Matches stuff that is translateable if it occurs in the right context
# (between tags).  This includes all characters and character entities.
# Note that this also matches   which needs to be handled as whitespace
# before this regexp is applied.
_CHARACTERS = lazy_re.compile(r'''
  (
    \w
    |
    [\!\@\#\$\%\^\*\(\)\-\=\_\+\[\]\{\}\\\|\;\:\'\"\,\.\/\?\`\~]
    |
    &(\#[0-9]+|\#x[0-9a-fA-F]+|[A-Za-z0-9]+);
  )+
  ''', re.MULTILINE | re.DOTALL | re.VERBOSE)

# Matches Total Recall's "replaceable" tags, which are just any text
# in capitals enclosed by delimiters like [] or [~~] or [$~~$] (e.g. [HELLO],
# [~HELLO~] and [$~HELLO~$]).
_REPLACEABLE = lazy_re.compile(r'\[(\$?\~)?(?P[A-Z0-9-_]+?)(\~\$?)?\]',
                               re.MULTILINE)


# Matches the silly [!]-prefixed "header" that is used in some TotalRecall
# templates.
_SILLY_HEADER = lazy_re.compile(r'\[!\]\ntitle\t(?P[^\n]+?)\n.+?\n\n'</span>,<br>                                 re.MULTILINE | re.DOTALL)<br> <br> <br> <span style='color:green'># Matches a comment that provides a description for the message it occurs in.</span><br> _DESCRIPTION_COMMENT = lazy_re.compile(<br>   r<span style='color:blue'>'<!--\s*desc\s*=\s*(?P<description>.+?)\s*-->'</span>, re.DOTALL)<br> <br> <span style='color:green'># Matches a comment which is used to break apart multiple messages.</span><br> _MESSAGE_BREAK_COMMENT = lazy_re.compile(r<span style='color:blue'>'<!--\s*message-break\s*-->'</span>,<br>                                          re.DOTALL)<br> <br> <span style='color:green'># Matches a comment which is used to prevent block tags from splitting a message</span><br> _MESSAGE_NO_BREAK_COMMENT = re.compile(r<span style='color:blue'>'<!--\s*message-no-break\s*-->'</span>,<br>                                        re.DOTALL)<br> <br> <br> _DEBUG = 0<br> <span style='color:red'>def</span> _DebugPrint(text):<br>   <span style='color:red'>if</span> _DEBUG:<br>     print(text.encode(<span style='color:blue'>'utf-8'</span>))<br> <br> <br> <span style='color:red'>class</span> HtmlChunks(object):<br>   <span style='color:blue'>''</span><span style='color:blue'>'A parser that knows how to break an HTML-like document into a list of</span><br>   chunks, where each chunk <span style='color:red'>is</span> either translateable <span style='color:red'>or</span> non-translateable.<br>   The chunks are unmodified sections of the original document, so concatenating<br>   the text of all chunks would result <span style='color:red'>in</span> the original document.<span style='color:blue'>''</span><span style='color:blue'>'</span><br> <br>   <span style='color:red'>def</span> InTranslateable(self):<br>     <span style='color:red'>return</span> self.last_translateable != -1<br> <br>   <span style='color:red'>def</span> Rest(self):<br>     <span style='color:red'>return</span> self.text_[self.current:]<br> <br>   <span style='color:red'>def</span> StartTranslateable(self):<br>     <span style='color:red'>assert</span> <span style='color:red'>not</span> self.InTranslateable()<br>     <span style='color:red'>if</span> self.current != 0:<br>       <span style='color:green'># Append a nontranslateable chunk</span><br>       chunk_text = self.text_[self.chunk_start : self.last_nontranslateable + 1]<br>       <span style='color:green'># Needed in the case where document starts with a translateable.</span><br>       <span style='color:red'>if</span> len(chunk_text) > 0:<br>         self.AddChunk(<span style='color:red'>False</span>, chunk_text)<br>     self.chunk_start = self.last_nontranslateable + 1<br>     self.last_translateable = self.current<br>     self.last_nontranslateable = -1<br> <br>   <span style='color:red'>def</span> EndTranslateable(self):<br>     <span style='color:red'>assert</span> self.InTranslateable()<br>     <span style='color:green'># Append a translateable chunk</span><br>     self.AddChunk(<span style='color:red'>True</span>,<br>                   self.text_[self.chunk_start : self.last_translateable + 1])<br>     self.chunk_start = self.last_translateable + 1<br>     self.last_translateable = -1<br>     self.last_nontranslateable = self.current<br> <br>   <span style='color:red'>def</span> AdvancePast(self, match):<br>     self.current += match.end()<br> <br>   <span style='color:red'>def</span> AddChunk(self, translateable, text):<br>     <span style='color:blue'>''</span><span style='color:blue'>'Adds a chunk to self, removing linebreaks and duplicate whitespace</span><br>     <span style='color:red'>if</span> appropriate.<br>     <span style='color:blue'>''</span><span style='color:blue'>'</span><br>     m = _DESCRIPTION_COMMENT.search(text)<br>     <span style='color:red'>if</span> m:<br>       self.last_description = m.group(<span style='color:blue'>'description'</span>)<br>       <span style='color:green'># Remove the description from the output text</span><br>       text = _DESCRIPTION_COMMENT.sub(<span style='color:blue'>''</span>, text)<br> <br>     m = _MESSAGE_BREAK_COMMENT.search(text)<br>     <span style='color:red'>if</span> m:<br>       <span style='color:green'># Remove the coment from the output text.  It should already effectively</span><br>       <span style='color:green'># break apart messages.</span><br>       text = _MESSAGE_BREAK_COMMENT.sub(<span style='color:blue'>''</span>, text)<br> <br>     <span style='color:red'>if</span> translateable <span style='color:red'>and</span> <span style='color:red'>not</span> self.last_element_ <span style='color:red'>in</span> _PREFORMATTED_TAGS:<br>       <span style='color:red'>if</span> self.fold_whitespace_:<br>         <span style='color:green'># Fold whitespace sequences if appropriate.  This is optional because it</span><br>         <span style='color:green'># alters the output strings.</span><br>         text = _FOLD_WHITESPACE.sub(<span style='color:blue'>' '</span>, text)<br>       <span style='color:red'>else</span>:<br>         text = text.replace(<span style='color:blue'>'\n'</span>, <span style='color:blue'>' '</span>)<br>         text = text.replace(<span style='color:blue'>'\r'</span>, <span style='color:blue'>' '</span>)<br>         <span style='color:green'># This whitespace folding doesn't work in all cases, thus the</span><br>         <span style='color:green'># fold_whitespace flag to support backwards compatibility.</span><br>         text = text.replace(<span style='color:blue'>' '</span>, <span style='color:blue'>' '</span>)<br>         text = text.replace(<span style='color:blue'>' '</span>, <span style='color:blue'>' '</span>)<br> <br>     <span style='color:red'>if</span> translateable:<br>       description = self.last_description<br>       self.last_description = <span style='color:blue'>''</span><br>     <span style='color:red'>else</span>:<br>       description = <span style='color:blue'>''</span><br> <br>     <span style='color:red'>if</span> text != <span style='color:blue'>''</span>:<br>       self.chunks_.append((translateable, text, description))<br> <br>   <span style='color:red'>def</span> Parse(self, text, fold_whitespace):<br>     <span style='color:blue'>''</span><span style='color:blue'>'Parses self.text_ into an intermediate format stored in self.chunks_</span><br>     which <span style='color:red'>is</span> translateable <span style='color:red'>and</span> nontranslateable chunks.  Also returns<br>     self.chunks_<br> <br>     Args:<br>       text: The HTML <span style='color:red'>for</span> parsing.<br>       fold_whitespace: Whether whitespace sequences should be folded into a<br>         single space.<br> <br>     <span style='color:red'>Return</span>:<br>       [chunk1, chunk2, chunk3, ...]  (instances of <span style='color:red'>class</span> Chunk)<br>     <span style='color:blue'>''</span><span style='color:blue'>'</span><br>     <span style='color:green'>#</span><br>     <span style='color:green'># Chunker state</span><br>     <span style='color:green'>#</span><br> <br>     self.text_ = text<br>     self.fold_whitespace_ = fold_whitespace<br> <br>     <span style='color:green'># A list of tuples (is_translateable, text) which represents the document</span><br>     <span style='color:green'># after chunking.</span><br>     self.chunks_ = []<br> <br>     <span style='color:green'># Start index of the last chunk, whether translateable or not</span><br>     self.chunk_start = 0<br> <br>     <span style='color:green'># Index of the last for-sure translateable character if we are parsing</span><br>     <span style='color:green'># a translateable chunk, -1 to indicate we are not in a translateable chunk.</span><br>     <span style='color:green'># This is needed so that we don't include trailing whitespace in the</span><br>     <span style='color:green'># translateable chunk (whitespace is neutral).</span><br>     self.last_translateable = -1<br> <br>     <span style='color:green'># Index of the last for-sure nontranslateable character if we are parsing</span><br>     <span style='color:green'># a nontranslateable chunk, -1 if we are not in a nontranslateable chunk.</span><br>     <span style='color:green'># This is needed to make sure we can group e.g. "<b>Hello</b> there"</span><br>     <span style='color:green'># together instead of just "Hello</b> there" which would be much worse</span><br>     <span style='color:green'># for translation.</span><br>     self.last_nontranslateable = -1<br> <br>     <span style='color:green'># Index of the character we're currently looking at.</span><br>     self.current = 0<br> <br>     <span style='color:green'># The name of the last block element parsed.</span><br>     self.last_element_ = <span style='color:blue'>''</span><br> <br>     <span style='color:green'># The last explicit description we found.</span><br>     self.last_description = <span style='color:blue'>''</span><br> <br>     <span style='color:green'># Whether no-break was the last chunk seen</span><br>     self.last_nobreak = <span style='color:red'>False</span><br> <br>     <span style='color:red'>while</span> self.current < len(self.text_):<br>       _DebugPrint(<span style='color:blue'>'REST: %s'</span> % self.text_[self.current:self.current+60])<br> <br>       m = _MESSAGE_NO_BREAK_COMMENT.match(self.Rest())<br>       <span style='color:red'>if</span> m:<br>         self.AdvancePast(m)<br>         self.last_nobreak = <span style='color:red'>True</span><br>         <span style='color:red'>continue</span><br> <br>       <span style='color:green'># Try to match whitespace</span><br>       m = _WHITESPACE.match(self.Rest())<br>       <span style='color:red'>if</span> m:<br>         <span style='color:green'># Whitespace is neutral, it just advances 'current' and does not switch</span><br>         <span style='color:green'># between translateable/nontranslateable.  If we are in a</span><br>         <span style='color:green'># nontranslateable section that extends to the current point, we extend</span><br>         <span style='color:green'># it to include the whitespace.  If we are in a translateable section,</span><br>         <span style='color:green'># we do not extend it until we find</span><br>         <span style='color:green'># more translateable parts, because we never want a translateable chunk</span><br>         <span style='color:green'># to end with whitespace.</span><br>         <span style='color:red'>if</span> (<span style='color:red'>not</span> self.InTranslateable() <span style='color:red'>and</span><br>             self.last_nontranslateable == self.current - 1):<br>           self.last_nontranslateable = self.current + m.end() - 1<br>         self.AdvancePast(m)<br>         <span style='color:red'>continue</span><br> <br>       <span style='color:green'># Then we try to match nontranslateables</span><br>       m = _NONTRANSLATEABLES.match(self.Rest())<br>       <span style='color:red'>if</span> m:<br>         <span style='color:red'>if</span> self.InTranslateable():<br>           self.EndTranslateable()<br>         self.last_nontranslateable = self.current + m.end() - 1<br>         self.AdvancePast(m)<br>         <span style='color:red'>continue</span><br> <br>       <span style='color:green'># Now match all other HTML element tags (opening, closing, or empty, we</span><br>       <span style='color:green'># don't care).</span><br>       m = _ELEMENT.match(self.Rest())<br>       <span style='color:red'>if</span> m:<br>         element_name = m.group(<span style='color:blue'>'element'</span>).lower()<br>         <span style='color:red'>if</span> element_name <span style='color:red'>in</span> _BLOCK_TAGS:<br>           self.last_element_ = element_name<br>           <span style='color:red'>if</span> self.InTranslateable():<br>             <span style='color:red'>if</span> self.last_nobreak:<br>               self.last_nobreak = <span style='color:red'>False</span><br>             <span style='color:red'>else</span>:<br>               self.EndTranslateable()<br> <br>           <span style='color:green'># Check for "special" elements, i.e. ones that have a translateable</span><br>           <span style='color:green'># attribute, and handle them correctly.  Note that all of the</span><br>           <span style='color:green'># "special" elements are block tags, so no need to check for this</span><br>           <span style='color:green'># if the tag is not a block tag.</span><br>           sm = _SPECIAL_ELEMENT.match(self.Rest())<br>           <span style='color:red'>if</span> sm:<br>             <span style='color:green'># Get the appropriate group name</span><br>             <span style='color:red'>for</span> group <span style='color:red'>in</span> sm.groupdict():<br>               <span style='color:red'>if</span> sm.groupdict()[group]:<br>                 <span style='color:red'>break</span><br> <br>             <span style='color:green'># First make a nontranslateable chunk up to and including the</span><br>             <span style='color:green'># quote before the translateable attribute value</span><br>             self.AddChunk(<span style='color:red'>False</span>, self.text_[<br>               self.chunk_start : self.current + sm.start(group)])<br>             <span style='color:green'># Then a translateable for the translateable bit</span><br>             self.AddChunk(<span style='color:red'>True</span>, self.Rest()[sm.start(group) : sm.end(group)])<br>             <span style='color:green'># Finally correct the data invariant for the parser</span><br>             self.chunk_start = self.current + sm.end(group)<br> <br>           self.last_nontranslateable = self.current + m.end() - 1<br>         <span style='color:red'>elif</span> self.InTranslateable():<br>           <span style='color:green'># We're in a translateable and the tag is an inline tag, so we</span><br>           <span style='color:green'># need to include it in the translateable.</span><br>           self.last_translateable = self.current + m.end() - 1<br>         self.AdvancePast(m)<br>         <span style='color:red'>continue</span><br> <br>       <span style='color:green'># Anything else we find must be translateable, so we advance one character</span><br>       <span style='color:green'># at a time until one of the above matches.</span><br>       <span style='color:red'>if</span> <span style='color:red'>not</span> self.InTranslateable():<br>         self.StartTranslateable()<br>       <span style='color:red'>else</span>:<br>         self.last_translateable = self.current<br>       self.current += 1<br> <br>     <span style='color:green'># Close the final chunk</span><br>     <span style='color:red'>if</span> self.InTranslateable():<br>       self.AddChunk(<span style='color:red'>True</span>, self.text_[self.chunk_start : ])<br>     <span style='color:red'>else</span>:<br>       self.AddChunk(<span style='color:red'>False</span>, self.text_[self.chunk_start : ])<br> <br>     <span style='color:red'>return</span> self.chunks_<br> <br> <br> <span style='color:red'>def</span> HtmlToMessage(html, include_block_tags=<span style='color:red'>False</span>, description=<span style='color:blue'>''</span>):<br>   <span style='color:blue'>''</span><span style='color:blue'>'Takes a bit of HTML, which must contain only "inline" HTML elements,</span><br>   <span style='color:red'>and</span> changes it into a tclib.Message.  This involves escaping any entities <span style='color:red'>and</span><br>   replacing any HTML code <span style='color:red'>with</span> placeholders.<br> <br>   <span style='color:red'>If</span> include_block_tags <span style='color:red'>is</span> <span style='color:red'>true</span>, no error will be given <span style='color:red'>if</span> block tags (e.g.<br>   <p> <span style='color:red'>or</span> <br>) are included <span style='color:red'>in</span> the HTML.<br> <br>   Args:<br>     html: <span style='color:blue'>'Hello <b>[USERNAME]</b>, how <i>are</i> you?'</span><br>     include_block_tags: <span style='color:red'>False</span><br> <br>   <span style='color:red'>Return</span>:<br>     tclib.Message(<span style='color:blue'>'Hello START_BOLD1USERNAMEEND_BOLD, '</span><br>                   <span style='color:blue'>'howNBSPSTART_ITALICareEND_ITALIC you?'</span>,<br>                   [ Placeholder(<span style='color:blue'>'START_BOLD'</span>, <span style='color:blue'>'<b>'</span>, <span style='color:blue'>''</span>),<br>                     Placeholder(<span style='color:blue'>'USERNAME'</span>, <span style='color:blue'>'[USERNAME]'</span>, <span style='color:blue'>''</span>),<br>                     Placeholder(<span style='color:blue'>'END_BOLD'</span>, <span style='color:blue'>'</b>'</span>, <span style='color:blue'>''</span>),<br>                     Placeholder(<span style='color:blue'>'START_ITALIC'</span>, <span style='color:blue'>'<i>'</span>, <span style='color:blue'>''</span>),<br>                     Placeholder(<span style='color:blue'>'END_ITALIC'</span>, <span style='color:blue'>'</i>'</span>, <span style='color:blue'>''</span>), ])<br>   <span style='color:blue'>''</span><span style='color:blue'>'</span><br>   <span style='color:green'># Approach is:</span><br>   <span style='color:green'># - first placeholderize, finding <elements>, [REPLACEABLES] and  </span><br>   <span style='color:green'># - then escape all character entities in text in-between placeholders</span><br> <br>   parts = []  <span style='color:green'># List of strings (for text chunks) and tuples (ID, original)</span><br>               <span style='color:green'># for placeholders</span><br> <br>   count_names = {}  <span style='color:green'># Map of base names to number of times used</span><br>   end_names = {}  <span style='color:green'># Map of base names to stack of end tags (for correct nesting)</span><br> <br>   <span style='color:red'>def</span> MakeNameClosure(base, type = <span style='color:blue'>''</span>):<br>     <span style='color:blue'>''</span><span style='color:blue'>'Returns a closure that can be called once all names have been allocated</span><br>     to <span style='color:red'>return</span> the final name of the placeholder.  This allows us to minimally<br>     number placeholders <span style='color:red'>for</span> non-overlap.<br> <br>     Also ensures that END_XXX_Y placeholders have the same Y <span style='color:red'>as</span> the<br>     corresponding BEGIN_XXX_Y placeholder when we have nested tags of the same<br>     type.<br> <br>     Args:<br>       base: <span style='color:blue'>'phname'</span><br>       type: <span style='color:blue'>''</span> | <span style='color:blue'>'begin'</span> | <span style='color:blue'>'end'</span><br> <br>     <span style='color:red'>Return</span>:<br>       Closure()<br>     <span style='color:blue'>''</span><span style='color:blue'>'</span><br>     name = base.upper()<br>     <span style='color:red'>if</span> type != <span style='color:blue'>''</span>:<br>       name = (<span style='color:blue'>'%s_%s'</span> % (type, base)).upper()<br> <br>     count_names.setdefault(name, 0)<br>     count_names[name] += 1<br> <br>     <span style='color:red'>def</span> MakeFinalName(name_ = name, index = count_names[name] - 1):<br>       <span style='color:red'>if</span> type.lower() == <span style='color:blue'>'end'</span> <span style='color:red'>and</span> end_names.get(base):<br>         <span style='color:red'>return</span> end_names[base].pop(-1)  <span style='color:green'># For correct nesting</span><br>       <span style='color:red'>if</span> count_names[name_] != 1:<br>         name_ = <span style='color:blue'>'%s_%s'</span> % (name_, _SUFFIXES[index])<br>         <span style='color:green'># We need to use a stack to ensure that the end-tag suffixes match</span><br>         <span style='color:green'># the begin-tag suffixes.  Only needed when more than one tag of the</span><br>         <span style='color:green'># same type.</span><br>         <span style='color:red'>if</span> type == <span style='color:blue'>'begin'</span>:<br>           end_name = (<span style='color:blue'>'END_%s_%s'</span> % (base, _SUFFIXES[index])).upper()<br>           <span style='color:red'>if</span> base <span style='color:red'>in</span> end_names:<br>             end_names[base].append(end_name)<br>           <span style='color:red'>else</span>:<br>             end_names[base] = [end_name]<br> <br>       <span style='color:red'>return</span> name_<br> <br>     <span style='color:red'>return</span> MakeFinalName<br> <br>   current = 0<br>   last_nobreak = <span style='color:red'>False</span><br> <br>   <span style='color:red'>while</span> current < len(html):<br>     m = _MESSAGE_NO_BREAK_COMMENT.match(html[current:])<br>     <span style='color:red'>if</span> m:<br>       last_nobreak = <span style='color:red'>True</span><br>       current += m.end()<br>       <span style='color:red'>continue</span><br> <br>     m = _NBSP.match(html[current:])<br>     <span style='color:red'>if</span> m:<br>       parts.append((MakeNameClosure(<span style='color:blue'>'SPACE'</span>), m.group()))<br>       current += m.end()<br>       <span style='color:red'>continue</span><br> <br>     m = _REPLACEABLE.match(html[current:])<br>     <span style='color:red'>if</span> m:<br>       <span style='color:green'># Replaceables allow - but placeholders don't, so replace - with _</span><br>       ph_name = MakeNameClosure(<span style='color:blue'>'X_%s_X'</span> % m.group(<span style='color:blue'>'name'</span>).replace(<span style='color:blue'>'-'</span>, <span style='color:blue'>'_'</span>))<br>       parts.append((ph_name, m.group()))<br>       current += m.end()<br>       <span style='color:red'>continue</span><br> <br>     m = _SPECIAL_ELEMENT.match(html[current:])<br>     <span style='color:red'>if</span> m:<br>       <span style='color:red'>if</span> <span style='color:red'>not</span> include_block_tags:<br>         <span style='color:red'>if</span> last_nobreak:<br>           last_nobreak = <span style='color:red'>False</span><br>         <span style='color:red'>else</span>:<br>           <span style='color:red'>raise</span> exception.BlockTagInTranslateableChunk(html)<br>       element_name = <span style='color:blue'>'block'</span>  <span style='color:green'># for simplification</span><br>       <span style='color:green'># Get the appropriate group name</span><br>       <span style='color:red'>for</span> group <span style='color:red'>in</span> m.groupdict():<br>         <span style='color:red'>if</span> m.groupdict()[group]:<br>           <span style='color:red'>break</span><br>       parts.append((MakeNameClosure(element_name, <span style='color:blue'>'begin'</span>),<br>                     html[current : current + m.start(group)]))<br>       parts.append(m.group(group))<br>       parts.append((MakeNameClosure(element_name, <span style='color:blue'>'end'</span>),<br>                     html[current + m.end(group) : current + m.end()]))<br>       current += m.end()<br>       <span style='color:red'>continue</span><br> <br>     m = _ELEMENT.match(html[current:])<br>     <span style='color:red'>if</span> m:<br>       element_name = m.group(<span style='color:blue'>'element'</span>).lower()<br>       <span style='color:red'>if</span> <span style='color:red'>not</span> include_block_tags <span style='color:red'>and</span> <span style='color:red'>not</span> element_name <span style='color:red'>in</span> _INLINE_TAGS:<br>         <span style='color:red'>if</span> last_nobreak:<br>           last_nobreak = <span style='color:red'>False</span><br>         <span style='color:red'>else</span>:<br>           <span style='color:red'>raise</span> exception.BlockTagInTranslateableChunk(html[current:])<br>       <span style='color:red'>if</span> element_name <span style='color:red'>in</span> _HTML_PLACEHOLDER_NAMES:  <span style='color:green'># use meaningful names</span><br>         element_name = _HTML_PLACEHOLDER_NAMES[element_name]<br> <br>       <span style='color:green'># Make a name for the placeholder</span><br>       type = <span style='color:blue'>''</span><br>       <span style='color:red'>if</span> <span style='color:red'>not</span> m.group(<span style='color:blue'>'empty'</span>):<br>         <span style='color:red'>if</span> m.group(<span style='color:blue'>'closing'</span>):<br>           type = <span style='color:blue'>'end'</span><br>         <span style='color:red'>else</span>:<br>           type = <span style='color:blue'>'begin'</span><br>       parts.append((MakeNameClosure(element_name, type), m.group()))<br>       current += m.end()<br>       <span style='color:red'>continue</span><br> <br>     <span style='color:red'>if</span> len(parts) <span style='color:red'>and</span> isinstance(parts[-1], six.string_types):<br>       parts[-1] += html[current]<br>     <span style='color:red'>else</span>:<br>       parts.append(html[current])<br>     current += 1<br> <br>   msg_text = <span style='color:blue'>''</span><br>   placeholders = []<br>   <span style='color:red'>for</span> part <span style='color:red'>in</span> parts:<br>     <span style='color:red'>if</span> isinstance(part, tuple):<br>       final_name = part[0]()<br>       original = part[1]<br>       msg_text += final_name<br>       placeholders.append(tclib.Placeholder(final_name, original, <span style='color:blue'>'(HTML code)'</span>))<br>     <span style='color:red'>else</span>:<br>       msg_text += part<br> <br>   msg = tclib.Message(text=msg_text, placeholders=placeholders,<br>                       description=description)<br>   content = msg.GetContent()<br>   <span style='color:red'>for</span> ix <span style='color:red'>in</span> range(len(content)):<br>     <span style='color:red'>if</span> isinstance(content[ix], six.string_types):<br>       content[ix] = util.UnescapeHtml(content[ix], replace_nbsp=<span style='color:red'>False</span>)<br> <br>   <span style='color:red'>return</span> msg<br> <br> <br> <span style='color:red'>class</span> TrHtml(interface.GathererBase):<br>   <span style='color:blue'>''</span><span style='color:blue'>'Represents a document or message in the template format used by</span><br>   Total Recall <span style='color:red'>for</span> HTML documents.<span style='color:blue'>''</span><span style='color:blue'>'</span><br> <br>   <span style='color:red'>def</span> __init__(self, *args, **kwargs):<br>     super(TrHtml, self).__init__(*args, **kwargs)<br>     self.have_parsed_ = <span style='color:red'>False</span><br>     self.skeleton_ = []  <span style='color:green'># list of strings and MessageClique objects</span><br>     self.fold_whitespace_ = <span style='color:red'>False</span><br> <br>   <span style='color:red'>def</span> SetAttributes(self, attrs):<br>     <span style='color:blue'>''</span><span style='color:blue'>'Sets node attributes used by the gatherer.</span><br> <br>     This checks the fold_whitespace attribute.<br> <br>     Args:<br>       attrs: The mapping of node attributes.<br>     <span style='color:blue'>''</span><span style='color:blue'>'</span><br>     self.fold_whitespace_ = (<span style='color:blue'>'fold_whitespace'</span> <span style='color:red'>in</span> attrs <span style='color:red'>and</span><br>                              attrs[<span style='color:blue'>'fold_whitespace'</span>] == <span style='color:blue'>'true'</span>)<br> <br>   <span style='color:red'>def</span> GetText(self):<br>     <span style='color:blue'>''</span><span style='color:blue'>'Returns the original text of the HTML document'</span><span style='color:blue'>''</span><br>     <span style='color:red'>return</span> self.text_<br> <br>   <span style='color:red'>def</span> GetTextualIds(self):<br>     <span style='color:red'>return</span> [self.extkey]<br> <br>   <span style='color:red'>def</span> GetCliques(self):<br>     <span style='color:blue'>''</span><span style='color:blue'>'Returns the message cliques for each translateable message in the</span><br>     document.<span style='color:blue'>''</span><span style='color:blue'>'</span><br>     <span style='color:red'>return</span> [x <span style='color:red'>for</span> x <span style='color:red'>in</span> self.skeleton_ <span style='color:red'>if</span> isinstance(x, clique.MessageClique)]<br> <br>   <span style='color:red'>def</span> Translate(self, lang, pseudo_if_not_available=<span style='color:red'>True</span>,<br>                 skeleton_gatherer=<span style='color:red'>None</span>, fallback_to_english=<span style='color:red'>False</span>):<br>     <span style='color:blue'>''</span><span style='color:blue'>'Returns this document with translateable messages filled with</span><br>     the translation <span style='color:red'>for</span> language <span style='color:blue'>'lang'</span>.<br> <br>     Args:<br>       lang: <span style='color:blue'>'en'</span><br>       pseudo_if_not_available: <span style='color:red'>True</span><br> <br>     <span style='color:red'>Return</span>:<br>       <span style='color:blue'>'ID_THIS_SECTION TYPE\n...BEGIN\n "Translated message"\n......\nEND</span><br> <br>     Raises:<br>       grit.exception.NotReady() <span style='color:red'>if</span> used before Parse() has been successfully<br>       called.<br>       grit.exception.NoSuchTranslation() <span style='color:red'>if</span> <span style='color:blue'>'pseudo_if_not_available'</span> <span style='color:red'>is</span> <span style='color:red'>false</span><br>       <span style='color:red'>and</span> there <span style='color:red'>is</span> no translation <span style='color:red'>for</span> the requested language.<br>     <span style='color:blue'>''</span><span style='color:blue'>'</span><br>     <span style='color:red'>if</span> len(self.skeleton_) == 0:<br>       <span style='color:red'>raise</span> exception.NotReady()<br> <br>     <span style='color:green'># TODO(joi) Implement support for skeleton gatherers here.</span><br> <br>     out = []<br>     <span style='color:red'>for</span> item <span style='color:red'>in</span> self.skeleton_:<br>       <span style='color:red'>if</span> isinstance(item, six.string_types):<br>         out.append(item)<br>       <span style='color:red'>else</span>:<br>         msg = item.MessageForLanguage(lang,<br>                                       pseudo_if_not_available,<br>                                       fallback_to_english)<br>         <span style='color:red'>for</span> content <span style='color:red'>in</span> msg.GetContent():<br>           <span style='color:red'>if</span> isinstance(content, tclib.Placeholder):<br>             out.append(content.GetOriginal())<br>           <span style='color:red'>else</span>:<br>             <span style='color:green'># We escape " characters to increase the chance that attributes</span><br>             <span style='color:green'># will be properly escaped.</span><br>             out.append(util.EscapeHtml(content, <span style='color:red'>True</span>))<br> <br>     <span style='color:red'>return</span> <span style='color:blue'>''</span>.join(out)<br> <br>   <span style='color:red'>def</span> Parse(self):<br>     <span style='color:red'>if</span> self.have_parsed_:<br>       <span style='color:red'>return</span><br>     self.have_parsed_ = <span style='color:red'>True</span><br> <br>     text = self._LoadInputFile()<br> <br>     <span style='color:green'># Ignore the BOM character if the document starts with one.</span><br>     <span style='color:red'>if</span> text.startswith(u<span style='color:blue'>'\ufeff'</span>):<br>       text = text[1:]<br> <br>     self.text_ = text<br> <br>     <span style='color:green'># Parsing is done in two phases:  First, we break the document into</span><br>     <span style='color:green'># translateable and nontranslateable chunks.  Second, we run through each</span><br>     <span style='color:green'># translateable chunk and insert placeholders for any HTML elements,</span><br>     <span style='color:green'># unescape escaped characters, etc.</span><br> <br>     <span style='color:green'># First handle the silly little [!]-prefixed header because it's not</span><br>     <span style='color:green'># handled by our HTML parsers.</span><br>     m = _SILLY_HEADER.match(text)<br>     <span style='color:red'>if</span> m:<br>       self.skeleton_.append(text[:m.start(<span style='color:blue'>'title'</span>)])<br>       self.skeleton_.append(self.uberclique.MakeClique(<br>         tclib.Message(text=text[m.start(<span style='color:blue'>'title'</span>):m.end(<span style='color:blue'>'title'</span>)])))<br>       self.skeleton_.append(text[m.end(<span style='color:blue'>'title'</span>) : m.end()])<br>       text = text[m.end():]<br> <br>     chunks = HtmlChunks().Parse(text, self.fold_whitespace_)<br> <br>     <span style='color:red'>for</span> chunk <span style='color:red'>in</span> chunks:<br>       <span style='color:red'>if</span> chunk[0]:  <span style='color:green'># Chunk is translateable</span><br>         self.skeleton_.append(self.uberclique.MakeClique(<br>           HtmlToMessage(chunk[1], description=chunk[2])))<br>       <span style='color:red'>else</span>:<br>         self.skeleton_.append(chunk[1])<br> <br>     <span style='color:green'># Go through the skeleton and change any messages that consist solely of</span><br>     <span style='color:green'># placeholders and whitespace into nontranslateable strings.</span><br>     <span style='color:red'>for</span> ix <span style='color:red'>in</span> range(len(self.skeleton_)):<br>       got_text = <span style='color:red'>False</span><br>       <span style='color:red'>if</span> isinstance(self.skeleton_[ix], clique.MessageClique):<br>         msg = self.skeleton_[ix].GetMessage()<br>         <span style='color:red'>for</span> item <span style='color:red'>in</span> msg.GetContent():<br>           <span style='color:red'>if</span> (isinstance(item, six.string_types)<br>               <span style='color:red'>and</span> _NON_WHITESPACE.search(item) <span style='color:red'>and</span> item != <span style='color:blue'>' '</span>):<br>             got_text = <span style='color:red'>True</span><br>             <span style='color:red'>break</span><br>         <span style='color:red'>if</span> <span style='color:red'>not</span> got_text:<br>           self.skeleton_[ix] = msg.GetRealContent()<br> <br>   <span style='color:red'>def</span> SubstituteMessages(self, substituter):<br>     <span style='color:blue'>''</span><span style='color:blue'>'Applies substitutions to all messages in the tree.</span><br> <br>     Goes through the skeleton <span style='color:red'>and</span> finds all MessageCliques.<br> <br>     Args:<br>       substituter: a grit.util.Substituter object.<br>     <span style='color:blue'>''</span><span style='color:blue'>'</span><br>     new_skel = []<br>     <span style='color:red'>for</span> chunk <span style='color:red'>in</span> self.skeleton_:<br>       <span style='color:red'>if</span> isinstance(chunk, clique.MessageClique):<br>         old_message = chunk.GetMessage()<br>         new_message = substituter.SubstituteMessage(old_message)<br>         <span style='color:red'>if</span> new_message <span style='color:red'>is</span> <span style='color:red'>not</span> old_message:<br>           new_skel.append(self.uberclique.MakeClique(new_message))<br>           <span style='color:red'>continue</span><br>       new_skel.append(chunk)<br>     self.skeleton_ = new_skel<br> </div><div align=center><br><table border=1 style='border: 1px solid black; border-collapse: collapse;'><tr><th colspan=3>Messung V0.5</th></tr><tr><td> <svg height='38' width='38' > <circle cx='19' cy='19' r='16' stroke='grey' fill='purple' fill-opacity='30%' stroke-linecap='round' stroke-width='3' stroke-dasharray='360' stroke-dashoffset='50' /> <text x='12' y='22' fill='red' font-size=6>C=93</text> </svg> </td><td> <svg height='38' width='38' > <circle cx='19' cy='19' r='16' stroke='grey' fill='purple' fill-opacity='30%' stroke-linecap='round' stroke-width='3' stroke-dasharray='360' stroke-dashoffset='57' /> <text x='12' y='22' fill='red' font-size=6>H=92</text> </svg> </td><td> <svg height='56' width='56' > <circle cx='28' cy='28' r='24' stroke='green' fill='purple' fill-opacity='30%' stroke-linecap='round' stroke-width='4' stroke-dasharray='360' stroke-dashoffset='57' /> <text x='18' y='32' fill='red' font-size=8>G=92</text> </svg> </td></tr></table></div></span><br> <h3><b>¤</b> Dauer der Verarbeitung: 0.7 Sekunden  <b>¤</b></h3> <p height="2" colspan="2" align="center"><span style="font-size: 3px;">*© Formatika GbR, Deutschland</span></p> </div> </td> <td valign="top" align="center" class="greenscreensmall"> <br><br><br> <br> <table width="20%"> <tr><td align="center"> <a href="index.jsp?content=directory"> <br>Wurzel<br> <img border="0" src="/Images/penguin.jpg" height=36 alt="" title="Wurzel"> </a> </td> </tr> <tr><td align="center"> <a href="index.jsp?content=search" title="Suchen"> <br>Suchen<br> <img src="/Images/find.png" height="48" alt="" border="0"> </a> </td> </tr> <tr><td align="left"><a href="index.jsp?content=directory&detail=products/Sources/formale%20Sprachen/PVS/" title="Projekt "><br>Beweissystem der NASA</a></td></tr> <tr><td align="left"><a href="index.jsp?content=directory&detail=products/Sources/formale%20Sprachen/Isabelle/" title="Projekt "><br>Beweissystem Isabelle</a></td></tr> <tr><td align="left"><a href="index.jsp?content=directory&detail=products/Sources/formale%20Sprachen/Cobol/Test-Suite/" title="Projekt "><br>NIST Cobol Testsuite</a></td></tr> <tr><td align="left"><a href="index.jsp?content=directory&detail=products/Sources/formale%20Sprachen/Fortran/f90gl-1.2.15/" title="Projekt "><br>Cephes Mathematical Library</a></td></tr> <tr><td align="left"><a href="index.jsp?content=directory&detail=products/Sources/formale%20Sprachen/VDM/" title="Projekt "><br>Wiener Entwicklungsmethode</a></td></tr> <tr><td align="center"> <br> <h2>Haftungshinweis</h2> <div align="justify" class="featuresmall">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.</div> <br> <h2>Bemerkung:</h2> <div align="justify" class="featuresmall"> Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.</div> <br> </td> </tr> </table> <br><br> <div> <br> <script src="https://formatika.de/base/formcheck.js"></script> <script> function checkform(form) { var res = true; res = res && isnotempty(form.file); res = res && isurl(form.file); return res; } </script> </div><br> <br> </td> </tr> </table> </div> <div class="printelement"> <script> warningpreview(); </script> </div> <p align=right class=hidden>2026-04-06</p> </td> </tr> <tr> <td width="100%" class="content"> <p>                                                                                                                                                                                                                                                                                                                                                                                                       </p> </td> </tr> </table> </td> <td width="17%" valign="top" class="storysmall"> <div> <table class="headlines"> <tr><td> <table border="0"> <tr><td><hr align="left" width="70%%"/></td></tr> <tr><td><h2 align="left">Neuigkeiten</h2></td></tr> <tr><td>     <a title="letzte Meldungen über diese Firma" href="index.jsp?content=aktuelles">Aktuelles</a></td></tr> <tr><td>     <a title="Spruch des Tages" href="index.jsp?content=motto">Motto des Tages</a></td></tr> <tr><td><hr align="left" width="70%%"/></td></tr> <tr><td><h2 align="left">Software</h2></td></tr> <tr><td>     <a title="Windows-Programme und andere" href="index.jsp?content=products">Produkte</a></td></tr> <tr><td>     <a title="Sammlung von Quellcodes" href="index.jsp?content=directory">Quellcodebibliothek</a></td></tr> <tr><td><hr align="left" width="70%%"/></td></tr> <tr><td><h2 align="left">Aktivitäten</h2></td></tr> <tr><td>     <a href='index.jsp?content=mobiles'>Artikel über Sicherheit</a></td></tr> <tr><td>     <a href='index.jsp?content=sslhelp'>Anleitung zur Aktivierung von SSL</a></td></tr> <tr><td><hr align="left" width="70%%"/></td></tr> <tr><td><h2 align="left">Muße</h2></td></tr> <tr><td>     <a title="kleine Reime" href="index.jsp?content=gedichte">Gedichte</a></td></tr> <tr><td>     <a title="einige Melodien" href="index.jsp?content=musik">Musik</a></td></tr> <tr><td>     <a title="einige Melodien" href="index.jsp?content=bilder">Bilder</a></td></tr> <tr><td><hr align="left" width="70%%"/></td></tr> <tr><td> <strong>Jenseits des Üblichen ....</strong> <br><a title="Anleitung zur Erforschung der Natur" href="index.jsp?content=flora"><img name='Image1' src='/Images/coconut%20small.png' vspace='7' width='60' height='50' vspace='7' alt='' title='Knack das Problem' ><img name='Image1' src='/Images/coconut%20small.png' vspace='7' width='60' height='50' vspace='7' alt='' title='Knack das Problem' ><img name='Image1' src='/Images/coconut%20small.png' vspace='7' width='60' height='50' vspace='7' alt='' title='Knack das Problem' ></a> </td></tr> <tr><td><hr align="left" width="90%%"/></td></tr> <tr><td><h2>Besucherstatistik</h2></td></tr> <tr> <td><a href="index.jsp?content=stats&detail=chart" target="_self"> <img width="168" src="/Images/Googlemap.png" border="0" alt="Besucherstatistik" title="Besucherstatistik" > </a> </td> </tr> <tr><td><hr align="left" width="90%%"/></td></tr> <tr><td><h2>Monitoring</h2></td></tr> <tr> <td><a href="https://uelk2599jehr.montastic.io" target="_blank"> <img src="https://uelk2599jehr.montastic.io/badge" width=96 alt="Montastic status badge"> </a> </td> </tr> </table> </td></tr> </table> </div> </td> </tr> </table> </td> <td width="3%"> </td> </tr> <tr> <td colspan="3" align="center"> <div> <br><br><br> <div> <table width="45%" align="center" class="screenelement"> <tr><td width="80%"><hr class="ruler" /></td></tr> <tr><td width="80%" align="center"> <span class="feature"> <a title="über den Urheber dieser Seite" href="index.jsp?content=impressum">Impressum</a>  | <a title="etwas mehr zur Ethik" href="index.jsp?content=gesellschaft&detail=ethik">Ethik und Gesetz</a>  | <a title="diese Dinge liegen außhalb unserer Verantwortung" href="index.jsp?content=haftung" class="style6">Haftungsausschluß</a>  | <a title="hier können Sie eine Nachricht absetzen" href="index.jsp?content=contact">Kontakt</a>  | <a title="ein Bild über den Seitenaufbau" href="index.jsp?content=sitemap">Seitenstruktur</a>  </span> | <span class="featuresmall"><sup>©</sup> 2026 JDD</span> | <img src='/Images/unknown.jpg' alt='' title="Seite erzeugt: Seite erzeugt: 2026-04-06 23:47:59" ontouchend="alert('Seite erzeugt: 2026-04-06 23:47:59');" onclick="alert('Seite erzeugt: 2026-04-06 23:47:59');" width=12 valign='middle'> </td></tr> </table> </div> </div> </td> </tr> </table> </body> </html>