"Fossies" - the Fresh Open Source Software Archive

Member "roundup-2.0.0/roundup/cgi/TAL/talgettext.py" (26 Aug 2019, 9561 Bytes) of package /linux/www/roundup-2.0.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "talgettext.py": 1.6.1_vs_2.0.0.

    1 #!/usr/bin/env python
    2 ##############################################################################
    3 #
    4 # Copyright (c) 2002 Zope Corporation and Contributors.
    5 # All Rights Reserved.
    6 #
    7 # This software is subject to the provisions of the Zope Public License,
    8 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
    9 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
   10 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   11 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
   12 # FOR A PARTICULAR PURPOSE.
   13 #
   14 ##############################################################################
   15 # Modifications for Roundup:
   16 # 1. commented out ITALES references
   17 # 2. escape quotes and line feeds in msgids
   18 # 3. don't collect empty msgids
   19 
   20 """Program to extract internationalization markup from Page Templates.
   21 
   22 Once you have marked up a Page Template file with i18n: namespace tags, use
   23 this program to extract GNU gettext .po file entries.
   24 
   25 Usage: talgettext.py [options] files
   26 Options:
   27     -h / --help
   28         Print this message and exit.
   29     -o / --output <file>
   30         Output the translation .po file to <file>.
   31     -u / --update <file>
   32         Update the existing translation <file> with any new translation strings
   33         found.
   34 """
   35 
   36 from __future__ import print_function
   37 import sys
   38 import time
   39 import getopt
   40 import traceback
   41 
   42 from roundup import __version__
   43 from roundup.cgi.TAL.HTMLTALParser import HTMLTALParser
   44 from roundup.cgi.TAL.TALInterpreter import TALInterpreter
   45 from roundup.cgi.TAL.DummyEngine import DummyEngine
   46 #from ITALES import ITALESEngine
   47 from roundup.cgi.TAL.TALDefs import TALESError
   48 
   49 pot_header = '''\
   50 # SOME DESCRIPTIVE TITLE.
   51 # Copyright (C) YEAR ORGANIZATION
   52 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
   53 #
   54 msgid ""
   55 msgstr ""
   56 "Project-Id-Version: PACKAGE VERSION\\n"
   57 "POT-Creation-Date: %(time)s\\n"
   58 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n"
   59 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n"
   60 "Language-Team: LANGUAGE <LL@li.org>\\n"
   61 "MIME-Version: 1.0\\n"
   62 "Content-Type: text/plain; charset=CHARSET\\n"
   63 "Content-Transfer-Encoding: ENCODING\\n"
   64 "Generated-By: talgettext.py %(version)s\\n"
   65 '''
   66 
   67 NLSTR = '"\n"'
   68 
   69 def usage(code, msg=''):
   70     # Python 2.1 required
   71     print(__doc__, file=sys.stderr)
   72     if msg:
   73         print(msg, file=sys.stderr)
   74     sys.exit(code)
   75 
   76 
   77 class POTALInterpreter(TALInterpreter):
   78     def translate(self, msgid, default, i18ndict=None, obj=None):
   79         # XXX is this right?
   80         if i18ndict is None:
   81             i18ndict = {}
   82         if obj:
   83             i18ndict.update(obj)
   84         # XXX Mmmh, it seems that sometimes the msgid is None; is that really
   85         # possible?
   86         if msgid is None:
   87             return None
   88         # XXX We need to pass in one of context or target_language
   89         return self.engine.translate(msgid, self.i18nContext.domain, i18ndict,
   90                                      position=self.position, default=default)
   91 
   92 
   93 class POEngine(DummyEngine):
   94     #__implements__ = ITALESEngine
   95 
   96     def __init__(self, macros=None):
   97         self.catalog = {}
   98         DummyEngine.__init__(self, macros)
   99 
  100     def evaluate(*args):
  101         return '' # who cares
  102 
  103     def evaluatePathOrVar(*args):
  104         return '' # who cares
  105 
  106     def evaluateSequence(self, expr):
  107         return (0,) # dummy
  108 
  109     def evaluateBoolean(self, expr):
  110         return True # dummy
  111 
  112     def translate(self, msgid, domain=None, mapping=None, default=None,
  113                   # XXX position is not part of the ITALESEngine
  114                   #     interface
  115                   position=None):
  116 
  117         if not msgid: return 'x'
  118 
  119         if domain not in self.catalog:
  120             self.catalog[domain] = {}
  121         domain = self.catalog[domain]
  122 
  123         if msgid not in domain:
  124             domain[msgid] = []
  125         domain[msgid].append((self.file, position))
  126         return 'x'
  127 
  128 
  129 class UpdatePOEngine(POEngine):
  130     """A slightly-less braindead POEngine which supports loading an existing
  131     .po file first."""
  132 
  133     def __init__ (self, macros=None, filename=None):
  134         POEngine.__init__(self, macros)
  135 
  136         self._filename = filename
  137         self._loadFile()
  138         self.base = self.catalog
  139         self.catalog = {}
  140 
  141     def __add(self, id, s, fuzzy):
  142         "Add a non-fuzzy translation to the dictionary."
  143         if not fuzzy and str:
  144             # check for multi-line values and munge them appropriately
  145             if '\n' in s:
  146                 lines = s.rstrip().split('\n')
  147                 s = NLSTR.join(lines)
  148             self.catalog[id] = s
  149 
  150     def _loadFile(self):
  151         # shamelessly cribbed from Python's Tools/i18n/msgfmt.py
  152         # 25-Mar-2003 Nathan R. Yergler (nathan@zope.org)
  153         # 14-Apr-2003 Hacked by Barry Warsaw (barry@zope.com)
  154 
  155         ID = 1
  156         STR = 2
  157 
  158         try:
  159             lines = open(self._filename).readlines()
  160         except IOError as msg:
  161             print(msg, file=sys.stderr)
  162             sys.exit(1)
  163 
  164         section = None
  165         fuzzy = False
  166 
  167         # Parse the catalog
  168         lno = 0
  169         for l in lines:
  170             lno += True
  171             # If we get a comment line after a msgstr, this is a new entry
  172             if l[0] == '#' and section == STR:
  173                 self.__add(msgid, msgstr, fuzzy)
  174                 section = None
  175                 fuzzy = False
  176             # Record a fuzzy mark
  177             if l[:2] == '#,' and l.find('fuzzy'):
  178                 fuzzy = True
  179             # Skip comments
  180             if l[0] == '#':
  181                 continue
  182             # Now we are in a msgid section, output previous section
  183             if l.startswith('msgid'):
  184                 if section == STR:
  185                     self.__add(msgid, msgstr, fuzzy)
  186                 section = ID
  187                 l = l[5:]
  188                 msgid = msgstr = ''
  189             # Now we are in a msgstr section
  190             elif l.startswith('msgstr'):
  191                 section = STR
  192                 l = l[6:]
  193             # Skip empty lines
  194             if not l.strip():
  195                 continue
  196             # XXX: Does this always follow Python escape semantics?
  197             l = eval(l)
  198             if section == ID:
  199                 msgid += l
  200             elif section == STR:
  201                 msgstr += '%s\n' % l
  202             else:
  203                 print('Syntax error on %s:%d' % (infile, lno),
  204                       'before:', file=sys.stderr)
  205                 print(l, file=sys.stderr)
  206                 sys.exit(1)
  207         # Add last entry
  208         if section == STR:
  209             self.__add(msgid, msgstr, fuzzy)
  210 
  211     def evaluate(self, expression):
  212         try:
  213             return POEngine.evaluate(self, expression)
  214         except TALESError:
  215             pass
  216 
  217     def evaluatePathOrVar(self, expr):
  218         return 'who cares'
  219 
  220     def translate(self, msgid, domain=None, mapping=None, default=None,
  221                   position=None):
  222         if msgid not in self.base:
  223             POEngine.translate(self, msgid, domain, mapping, default, position)
  224         return 'x'
  225 
  226 
  227 def main():
  228     try:
  229         opts, args = getopt.getopt(
  230             sys.argv[1:],
  231             'ho:u:',
  232             ['help', 'output=', 'update='])
  233     except getopt.error as msg:
  234         usage(1, msg)
  235 
  236     outfile = None
  237     engine = None
  238     update_mode = False
  239     for opt, arg in opts:
  240         if opt in ('-h', '--help'):
  241             usage(0)
  242         elif opt in ('-o', '--output'):
  243             outfile = arg
  244         elif opt in ('-u', '--update'):
  245             update_mode = True
  246             if outfile is None:
  247                 outfile = arg
  248             engine = UpdatePOEngine(filename=arg)
  249 
  250     if not args:
  251         print('nothing to do')
  252         return
  253 
  254     # We don't care about the rendered output of the .pt file
  255     class Devnull:
  256         def write(self, s):
  257             pass
  258 
  259     # check if we've already instantiated an engine;
  260     # if not, use the stupidest one available
  261     if not engine:
  262         engine = POEngine()
  263 
  264     # process each file specified
  265     for filename in args:
  266         try:
  267             engine.file = filename
  268             p = HTMLTALParser()
  269             p.parseFile(filename)
  270             program, macros = p.getCode()
  271             POTALInterpreter(program, macros, engine, stream=Devnull(),
  272                              metal=False)()
  273         except: # Hee hee, I love bare excepts!
  274             print('There was an error processing', filename)
  275             traceback.print_exc()
  276 
  277     # Now output the keys in the engine.  Write them to a file if --output or
  278     # --update was specified; otherwise use standard out.
  279     if (outfile is None):
  280         outfile = sys.stdout
  281     else:
  282         outfile = open(outfile, update_mode and "a" or "w")
  283 
  284     catalog = {}
  285     for domain in engine.catalog.keys():
  286         catalog.update(engine.catalog[domain])
  287 
  288     messages = catalog.copy()
  289     try:
  290         messages.update(engine.base)
  291     except AttributeError:
  292         pass
  293     if '' not in messages:
  294         print(pot_header % {'time': time.ctime(),
  295                             'version': __version__}, file=outfile)
  296 
  297     # XXX: You should not sort by msgid, but by filename and position. (SR)
  298     msgids = sorted(catalog.keys())
  299     for msgid in msgids:
  300         positions = catalog[msgid]
  301         for filename, position in positions:
  302             outfile.write('#: %s:%s\n' % (filename, position[0]))
  303 
  304         outfile.write('msgid "%s"\n'
  305             % msgid.replace('"', '\\"').replace("\n", '\\n"\n"'))
  306         outfile.write('msgstr ""\n')
  307         outfile.write('\n')
  308 
  309 
  310 if __name__ == '__main__':
  311     main()