i18n/mki18n.py
changeset 1731 6ebd9c40b2be
parent 1730 64d8f52bc8c8
child 1734 750eeb7230a1
--- a/i18n/mki18n.py	Mon Aug 14 19:13:01 2017 +0300
+++ b/i18n/mki18n.py	Mon Aug 14 21:09:48 2017 +0300
@@ -1,503 +1,503 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-#
-#   PYTHON MODULE:     MKI18N.PY
-#                      =========
-#
-#   Abstract:         Make Internationalization (i18n) files for an application.
-#
-#   Copyright Pierre Rouleau. 2003. Released to public domain.
-#
-#   Last update: Saturday, November 8, 2003. @ 15:55:18.
-#
-#   File: ROUP2003N01::C:/dev/python/mki18n.py
-#
-#   RCS $Header: //software/official/MKS/MKS_SI/TV_NT/dev/Python/rcs/mki18n.py 1.5 2003/11/05 19:40:04 PRouleau Exp $
-#
-#   Update history:
-#
-#   - File created: Saturday, June 7, 2003. by Pierre Rouleau
-#   - 10/06/03 rcs : RCS Revision 1.1  2003/06/10 10:06:12  PRouleau
-#   - 10/06/03 rcs : RCS Initial revision
-#   - 23/08/03 rcs : RCS Revision 1.2  2003/06/10 10:54:27  PRouleau
-#   - 23/08/03 P.R.: [code:fix] : The strings encoded in this file are encode in iso-8859-1 format.  Added the encoding
-#                    notification to Python to comply with Python's 2.3 PEP 263.
-#   - 23/08/03 P.R.: [feature:new] : Added the '-e' switch which is used to force the creation of the empty English .mo file.
-#   - 22/10/03 P.R.: [code] : incorporated utility functions in here to make script self sufficient.
-#   - 05/11/03 rcs : RCS Revision 1.4  2003/10/22 06:39:31  PRouleau
-#   - 05/11/03 P.R.: [code:fix] : included the unixpath() in this file.
-#   - 08/11/03 rcs : RCS Revision 1.5  2003/11/05 19:40:04  PRouleau
-#
-#   RCS $Log: $
-#
-#
-# -----------------------------------------------------------------------------
-"""
-mki18n allows you to internationalize your software.  You can use it to
-create the GNU .po files (Portable Object) and the compiled .mo files
-(Machine Object).
-
-mki18n module can be used from the command line or from within a script (see
-the Usage at the end of this page).
-
-    Table of Contents
-    -----------------
-
-    makePO()             -- Build the Portable Object file for the application --
-    catPO()              -- Concatenate one or several PO files with the application domain files. --
-    makeMO()             -- Compile the Portable Object files into the Machine Object stored in the right location. --
-    printUsage           -- Displays how to use this script from the command line --
-
-    Scriptexecution      -- Runs when invoked from the command line --
-
-
-NOTE:  this module uses GNU gettext utilities.
-
-You can get the gettext tools from the following sites:
-
-   - `GNU FTP site for gettetx`_ where several versions (0.10.40, 0.11.2, 0.11.5 and 0.12.1) are available.
-     Note  that you need to use `GNU libiconv`_ to use this. Get it from the `GNU
-     libiconv  ftp site`_ and get version 1.9.1 or later. Get the Windows .ZIP
-     files and install the packages inside c:/gnu. All binaries will be stored
-     inside  c:/gnu/bin.  Just  put c:/gnu/bin inside your PATH. You will need
-     the following files:
-
-      - `gettext-runtime-0.12.1.bin.woe32.zip`_
-      - `gettext-tools-0.12.1.bin.woe32.zip`_
-      - `libiconv-1.9.1.bin.woe32.zip`_
-
-
-.. _GNU libiconv:                            http://www.gnu.org/software/libiconv/
-.. _GNU libiconv ftp site:                   http://www.ibiblio.org/pub/gnu/libiconv/
-.. _gettext-runtime-0.12.1.bin.woe32.zip:    ftp://ftp.gnu.org/gnu/gettext/gettext-runtime-0.12.1.bin.woe32.zip
-.. _gettext-tools-0.12.1.bin.woe32.zip:      ftp://ftp.gnu.org/gnu/gettext/gettext-tools-0.12.1.bin.woe32.zip
-.. _libiconv-1.9.1.bin.woe32.zip:            http://www.ibiblio.org/pub/gnu/libiconv/libiconv-1.9.1.bin.woe32.zip
-
-"""
-# -----------------------------------------------------------------------------
-# Module Import
-# -------------
-#
-import os
-import sys
-import wx
-import re
-
-# -----------------------------------------------------------------------------
-# Global variables
-# ----------------
-#
-
-__author__ = "Pierre Rouleau"
-__version__= "$Revision: 1.5 $"
-
-# -----------------------------------------------------------------------------
-
-def getlanguageDict():
-    languageDict = {}
-
-    if wx.VERSION >= (3, 0, 0):
-        app = wx.App()
-    else:
-        app = wx.PySimpleApp()
-
-    for lang in [x for x in dir(wx) if x.startswith("LANGUAGE")]:
-        i = wx.Locale(wx.LANGUAGE_DEFAULT).GetLanguageInfo(getattr(wx, lang))
-        if i:
-            languageDict[i.CanonicalName] = i.Description
-
-    return languageDict
-
-
-
-def processCustomFiles(filein, fileout, regexp, prefix = ''):
-    appfil_file = open(filein, 'r')
-    messages_file = open(fileout, 'r')
-    messages = messages_file.read()
-    messages_file.close()
-    messages_file = open(fileout, 'a')
-    messages_file.write('\n')
-    messages_file.write('#: %s\n' % prefix)
-    messages_file.write('\n')
-
-    words_found = {}
-    for filepath in appfil_file.xreadlines():
-        code_file = open(filepath.strip(), 'r')
-        for match in regexp.finditer(code_file.read()):
-            word = match.group(1)
-            if not words_found.get(word, False) and messages.find("msgid \"%s\"\nmsgstr \"\"" % word) == -1:
-                words_found[word] = True
-                messages_file.write('\n')
-                messages_file.write("msgid \"%s\"\n"%word)
-                messages_file.write("msgstr \"\"\n")
-        code_file.close()
-
-    messages_file.close()
-    appfil_file.close()
-
-
-# -----------------------------------------------------------------------------
-# m a k e P O ( )         -- Build the Portable Object file for the application --
-# ^^^^^^^^^^^^^^^
-#
-def makePO(applicationDirectoryPath,  applicationDomain=None, verbose=0) :
-    """Build the Portable Object Template file for the application.
-
-    makePO builds the .pot file for the application stored inside
-    a specified directory by running xgettext for all application source
-    files.  It finds the name of all files by looking for a file called 'app.fil'.
-    If this file does not exists, makePo raises an IOError exception.
-    By default the application domain (the application
-    name) is the same as the directory name but it can be overridden by the
-    'applicationDomain' argument.
-
-    makePO always creates a new file called messages.pot.  If it finds files
-    of the form app_xx.po where 'app' is the application name and 'xx' is one
-    of the ISO 639 two-letter language codes, makePO resynchronizes those
-    files with the latest extracted strings (now contained in messages.pot).
-    This process updates all line location number in the language-specific
-    .po files and may also create new entries for translation (or comment out
-    some).  The .po file is not changed, instead a new file is created with
-    the .new extension appended to the name of the .po file.
-
-    By default the function does not display what it is doing.  Set the
-    verbose argument to 1 to force it to print its commands.
-    """
-
-    if applicationDomain is None:
-        applicationName = fileBaseOf(applicationDirectoryPath,withPath=0)
-    else:
-        applicationName = applicationDomain
-    currentDir = os.getcwd()
-    os.chdir(applicationDirectoryPath)
-    filelist = 'app.fil'
-    if not os.path.exists(filelist):
-        raise IOError(2,'No module file: ' % filelist)
-
-    fileout = 'messages.pot'
-    # Steps:
-    #  Use xgettext to parse all application modules
-    #  The following switches are used:
-    #
-    #   -s                          : sort output by string content (easier to use when we need to merge several .po files)
-    #   --files-from=app.fil        : The list of files is taken from the file: app.fil
-    #   --output=                   : specifies the name of the output file (using a .pot extension)
-    cmd = 'xgettext -s --no-wrap --language=Python --files-from=' + filelist + ' --output=' + fileout + ' --package-name ' + applicationName
-    if verbose: print cmd
-    os.system(cmd)
-
-    XSD_STRING_MODEL = re.compile("<xsd\:(?:element|attribute) name=\"([^\"]*)\"[^\>]*\>")
-    processCustomFiles(filelist, fileout, XSD_STRING_MODEL, 'Extra XSD strings')
-
-    XML_TC6_STRING_MODEL = re.compile("<documentation>\s*<xhtml\:p><!\[CDATA\[([^\]]*)\]\]></xhtml\:p>\s*</documentation>", re.MULTILINE | re.DOTALL)
-    processCustomFiles(filelist, fileout, XML_TC6_STRING_MODEL, 'Extra TC6 documentation strings')
-
-    # generate messages.po
-    cmd = 'msginit --no-wrap --no-translator -i %s -l en_US.UTF-8 -o messages.po' % (fileout)
-    if verbose: print cmd
-    os.system(cmd)
-
-    languageDict = getlanguageDict()
-
-    for langCode in languageDict.keys():
-        if langCode == 'en':
-            pass
-        else:
-            langPOfileName = "%s_%s.po" % (applicationName , langCode)
-            if os.path.exists(langPOfileName):
-                cmd = 'msgmerge -s --no-wrap "%s" %s > "%s.new"' % (langPOfileName, fileout, langPOfileName)
-                if verbose: print cmd
-                os.system(cmd)
-    os.chdir(currentDir)
-
-# -----------------------------------------------------------------------------
-# c a t P O ( )         -- Concatenate one or several PO files with the application domain files. --
-# ^^^^^^^^^^^^^
-#
-def catPO(applicationDirectoryPath, listOf_extraPo, applicationDomain=None, targetDir=None, verbose=0) :
-    """Concatenate one or several PO files with the application domain files.
-    """
-
-    if applicationDomain is None:
-        applicationName = fileBaseOf(applicationDirectoryPath,withPath=0)
-    else:
-        applicationName = applicationDomain
-    currentDir = os.getcwd()
-    os.chdir(applicationDirectoryPath)
-
-    languageDict = getlanguageDict()
-
-    for langCode in languageDict.keys():
-        if langCode == 'en':
-            pass
-        else:
-            langPOfileName = "%s_%s.po" % (applicationName , langCode)
-            if os.path.exists(langPOfileName):
-                fileList = ''
-                for fileName in listOf_extraPo:
-                    fileList += ("%s_%s.po " % (fileName,langCode))
-                cmd = "msgcat -s --no-wrap %s %s > %s.cat" % (langPOfileName, fileList, langPOfileName)
-                if verbose: print cmd
-                os.system(cmd)
-                if targetDir is None:
-                    pass
-                else:
-                    mo_targetDir = "%s/%s/LC_MESSAGES" % (targetDir,langCode)
-                    cmd = "msgfmt --output-file=%s/%s.mo %s_%s.po.cat" % (mo_targetDir,applicationName,applicationName,langCode)
-                    if verbose: print cmd
-                    os.system(cmd)
-    os.chdir(currentDir)
-
-# -----------------------------------------------------------------------------
-# m a k e M O ( )         -- Compile the Portable Object files into the Machine Object stored in the right location. --
-# ^^^^^^^^^^^^^^^
-#
-def makeMO(applicationDirectoryPath,targetDir='./locale',applicationDomain=None, verbose=0, forceEnglish=0) :
-    """Compile the Portable Object files into the Machine Object stored in the right location.
-
-    makeMO converts all translated language-specific PO files located inside
-    the  application directory into the binary .MO files stored inside the
-    LC_MESSAGES sub-directory for the found locale files.
-
-    makeMO searches for all files that have a name of the form 'app_xx.po'
-    inside the application directory specified by the first argument.  The
-    'app' is the application domain name (that can be specified by the
-    applicationDomain argument or is taken from the directory name). The 'xx'
-    corresponds to one of the ISO 639 two-letter language codes.
-
-    makeMo stores the resulting files inside a sub-directory of `targetDir`
-    called xx/LC_MESSAGES where 'xx' corresponds to the 2-letter language
-    code.
-    """
-    if targetDir is None:
-        targetDir = './locale'
-    if verbose:
-        print "Target directory for .mo files is: %s" % targetDir
-
-    if applicationDomain is None:
-        applicationName = fileBaseOf(applicationDirectoryPath,withPath=0)
-    else:
-        applicationName = applicationDomain
-    currentDir = os.getcwd()
-    os.chdir(applicationDirectoryPath)
-
-    languageDict = getlanguageDict()
-
-    for langCode in languageDict.keys():
-        if (langCode == 'en') and (forceEnglish==0):
-            pass
-        else:
-            langPOfileName = "%s_%s.po" % (applicationName , langCode)
-            if os.path.exists(langPOfileName):
-                mo_targetDir = "%s/%s/LC_MESSAGES" % (targetDir,langCode)
-                if not os.path.exists(mo_targetDir):
-                    mkdir(mo_targetDir)
-                cmd = 'msgfmt --output-file="%s/%s.mo" "%s_%s.po"' % (mo_targetDir,applicationName,applicationName,langCode)
-                if verbose: print cmd
-                os.system(cmd)
-    os.chdir(currentDir)
-
-# -----------------------------------------------------------------------------
-# p r i n t U s a g e         -- Displays how to use this script from the command line --
-# ^^^^^^^^^^^^^^^^^^^
-#
-def printUsage(errorMsg=None) :
-    """Displays how to use this script from the command line."""
-    print """
-    ##################################################################################
-    #   mki18n :   Make internationalization files.                                  #
-    #              Uses the GNU gettext system to create PO (Portable Object) files  #
-    #              from source code, coimpile PO into MO (Machine Object) files.     #
-    #              Supports C,C++,Python source files.                               #
-    #                                                                                #
-    #   Usage: mki18n {OPTION} [appDirPath]                                          #
-    #                                                                                #
-    #   Options:                                                                     #
-    #     -e               : When -m is used, forces English .mo file creation       #
-    #     -h               : prints this help                                        #
-    #     -m               : make MO from existing PO files                          #
-    #     -p               : make PO, update PO files: Creates a new messages.pot    #
-    #                        file. Creates a dom_xx.po.new for every existing        #
-    #                        language specific .po file. ('xx' stands for the ISO639 #
-    #                        two-letter language code and 'dom' stands for the       #
-    #                        application domain name).  mki18n requires that you     #
-    #                        write a 'app.fil' file  which contains the list of all  #
-    #                        source code to parse.                                   #
-    #     -v               : verbose (prints comments while running)                 #
-    #     --domain=appName : specifies the application domain name.  By default      #
-    #                        the directory name is used.                             #
-    #     --moTarget=dir : specifies the directory where .mo files are stored.       #
-    #                      If not specified, the target is './locale'                #
-    #                                                                                #
-    #   You must specify one of the -p or -m option to perform the work.  You can    #
-    #   specify the path of the target application.  If you leave it out mki18n      #
-    #   will use the current directory as the application main directory.            #
-    #                                                                                #
-    ##################################################################################"""
-    if errorMsg:
-        print "\n   ERROR: %s" % errorMsg
-
-# -----------------------------------------------------------------------------
-# f i l e B a s e O f ( )         -- Return base name of filename --
-# ^^^^^^^^^^^^^^^^^^^^^^^
-#
-def fileBaseOf(filename,withPath=0) :
-   """fileBaseOf(filename,withPath) ---> string
-
-   Return base name of filename.  The returned string never includes the extension.
-   Use os.path.basename() to return the basename with the extension.  The
-   second argument is optional.  If specified and if set to 'true' (non zero)
-   the string returned contains the full path of the file name.  Otherwise the
-   path is excluded.
-
-   [Example]
-   >>> fn = 'd:/dev/telepath/tvapp/code/test.html'
-   >>> fileBaseOf(fn)
-   'test'
-   >>> fileBaseOf(fn)
-   'test'
-   >>> fileBaseOf(fn,1)
-   'd:/dev/telepath/tvapp/code/test'
-   >>> fileBaseOf(fn,0)
-   'test'
-   >>> fn = 'abcdef'
-   >>> fileBaseOf(fn)
-   'abcdef'
-   >>> fileBaseOf(fn,1)
-   'abcdef'
-   >>> fn = "abcdef."
-   >>> fileBaseOf(fn)
-   'abcdef'
-   >>> fileBaseOf(fn,1)
-   'abcdef'
-   """
-   pos = filename.rfind('.')
-   if pos > 0:
-      filename = filename[:pos]
-   if withPath:
-      return filename
-   else:
-      return os.path.basename(filename)
-# -----------------------------------------------------------------------------
-# m k d i r ( )         -- Create a directory (and possibly the entire tree) --
-# ^^^^^^^^^^^^^
-#
-def mkdir(directory) :
-   """Create a directory (and possibly the entire tree).
-
-   The os.mkdir() will fail to create a directory if one of the
-   directory in the specified path does not exist.  mkdir()
-   solves this problem.  It creates every intermediate directory
-   required to create the final path. Under Unix, the function
-   only supports forward slash separator, but under Windows and MacOS
-   the function supports the forward slash and the OS separator (backslash
-   under windows).
-   """
-
-   # translate the path separators
-   directory = unixpath(directory)
-   # build a list of all directory elements
-   aList = filter(lambda x: len(x)>0, directory.split('/'))
-   theLen = len(aList)
-   # if the first element is a Windows-style disk drive
-   # concatenate it with the first directory
-   if aList[0].endswith(':'):
-      if theLen > 1:
-         aList[1] = aList[0] + '/' + aList[1]
-         del aList[0]
-         theLen -= 1
-   # if the original directory starts at root,
-   # make sure the first element of the list
-   # starts at root too
-   if directory[0] == '/':
-      aList[0] = '/' + aList[0]
-   # Now iterate through the list, check if the
-   # directory exists and if not create it
-   theDir = ''
-   for i in range(theLen):
-      theDir += aList[i]
-      if not os.path.exists(theDir):
-         os.mkdir(theDir)
-      theDir += '/'
-
-# -----------------------------------------------------------------------------
-# u n i x p a t h ( )         -- Return a path name that contains Unix separator. --
-# ^^^^^^^^^^^^^^^^^^^
-#
-def unixpath(thePath) :
-   r"""Return a path name that contains Unix separator.
-
-   [Example]
-   >>> unixpath(r"d:\test")
-   'd:/test'
-   >>> unixpath("d:/test/file.txt")
-   'd:/test/file.txt'
-   >>>
-   """
-   thePath = os.path.normpath(thePath)
-   if os.sep == '/':
-      return thePath
-   else:
-      return thePath.replace(os.sep,'/')
-
-# -----------------------------------------------------------------------------
-
-# S c r i p t   e x e c u t i o n               -- Runs when invoked from the command line --
-# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-#
-if __name__ == "__main__":
-    import getopt     # command line parsing
-    argc = len(sys.argv)
-    if argc == 1:
-        printUsage('Missing argument: specify at least one of -m or -p (or both).')
-        sys.exit(1)
-    # If there is some arguments, parse the command line
-    validOptions     = "ehmpv"
-    validLongOptions = ['domain=', 'moTarget=']
-    option = {}
-    option['forceEnglish'] = 0
-    option['mo'] = 0
-    option['po'] = 0
-    option['verbose'] = 0
-    option['domain'] = None
-    option['moTarget'] = None
-    try:
-        optionList,pargs = getopt.getopt(sys.argv[1:],validOptions,validLongOptions)
-    except getopt.GetoptError, e:
-        printUsage(e[0])
-        sys.exit(1)
-    for (opt,val) in optionList:
-        if  (opt == '-h'):
-            printUsage()
-            sys.exit(0)
-        elif (opt == '-e'):         option['forceEnglish'] = 1
-        elif (opt == '-m'):         option['mo'] = 1
-        elif (opt == '-p'):         option['po'] = 1
-        elif (opt == '-v'):         option['verbose'] = 1
-        elif (opt == '--domain'):   option['domain'] = val
-        elif (opt == '--moTarget'): option['moTarget'] = val
-    if len(pargs) == 0:
-        appDirPath = os.getcwd()
-        if option['verbose']:
-            print "No project directory given. Using current one:  %s" % appDirPath
-    elif len(pargs) == 1:
-        appDirPath = pargs[0]
-    else:
-        printUsage('Too many arguments (%u).  Use double quotes if you have space in directory name' % len(pargs))
-        sys.exit(1)
-    if option['domain'] is None:
-        # If no domain specified, use the name of the target directory
-        option['domain'] = fileBaseOf(appDirPath)
-    if option['verbose']:
-        print "Application domain used is: '%s'" % option['domain']
-    if option['po']:
-        try:
-            makePO(appDirPath,option['domain'],option['verbose'])
-        except IOError, e:
-            printUsage(e[1] + '\n   You must write a file app.fil that contains the list of all files to parse.')
-    if option['mo']:
-        makeMO(appDirPath,option['moTarget'],option['domain'],option['verbose'],option['forceEnglish'])
-    sys.exit(1)
-
-
-# -----------------------------------------------------------------------------
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+#
+#   PYTHON MODULE:     MKI18N.PY
+#                      =========
+#
+#   Abstract:         Make Internationalization (i18n) files for an application.
+#
+#   Copyright Pierre Rouleau. 2003. Released to public domain.
+#
+#   Last update: Saturday, November 8, 2003. @ 15:55:18.
+#
+#   File: ROUP2003N01::C:/dev/python/mki18n.py
+#
+#   RCS $Header: //software/official/MKS/MKS_SI/TV_NT/dev/Python/rcs/mki18n.py 1.5 2003/11/05 19:40:04 PRouleau Exp $
+#
+#   Update history:
+#
+#   - File created: Saturday, June 7, 2003. by Pierre Rouleau
+#   - 10/06/03 rcs : RCS Revision 1.1  2003/06/10 10:06:12  PRouleau
+#   - 10/06/03 rcs : RCS Initial revision
+#   - 23/08/03 rcs : RCS Revision 1.2  2003/06/10 10:54:27  PRouleau
+#   - 23/08/03 P.R.: [code:fix] : The strings encoded in this file are encode in iso-8859-1 format.  Added the encoding
+#                    notification to Python to comply with Python's 2.3 PEP 263.
+#   - 23/08/03 P.R.: [feature:new] : Added the '-e' switch which is used to force the creation of the empty English .mo file.
+#   - 22/10/03 P.R.: [code] : incorporated utility functions in here to make script self sufficient.
+#   - 05/11/03 rcs : RCS Revision 1.4  2003/10/22 06:39:31  PRouleau
+#   - 05/11/03 P.R.: [code:fix] : included the unixpath() in this file.
+#   - 08/11/03 rcs : RCS Revision 1.5  2003/11/05 19:40:04  PRouleau
+#
+#   RCS $Log: $
+#
+#
+# -----------------------------------------------------------------------------
+"""
+mki18n allows you to internationalize your software.  You can use it to
+create the GNU .po files (Portable Object) and the compiled .mo files
+(Machine Object).
+
+mki18n module can be used from the command line or from within a script (see
+the Usage at the end of this page).
+
+    Table of Contents
+    -----------------
+
+    makePO()             -- Build the Portable Object file for the application --
+    catPO()              -- Concatenate one or several PO files with the application domain files. --
+    makeMO()             -- Compile the Portable Object files into the Machine Object stored in the right location. --
+    printUsage           -- Displays how to use this script from the command line --
+
+    Scriptexecution      -- Runs when invoked from the command line --
+
+
+NOTE:  this module uses GNU gettext utilities.
+
+You can get the gettext tools from the following sites:
+
+   - `GNU FTP site for gettetx`_ where several versions (0.10.40, 0.11.2, 0.11.5 and 0.12.1) are available.
+     Note  that you need to use `GNU libiconv`_ to use this. Get it from the `GNU
+     libiconv  ftp site`_ and get version 1.9.1 or later. Get the Windows .ZIP
+     files and install the packages inside c:/gnu. All binaries will be stored
+     inside  c:/gnu/bin.  Just  put c:/gnu/bin inside your PATH. You will need
+     the following files:
+
+      - `gettext-runtime-0.12.1.bin.woe32.zip`_
+      - `gettext-tools-0.12.1.bin.woe32.zip`_
+      - `libiconv-1.9.1.bin.woe32.zip`_
+
+
+.. _GNU libiconv:                            http://www.gnu.org/software/libiconv/
+.. _GNU libiconv ftp site:                   http://www.ibiblio.org/pub/gnu/libiconv/
+.. _gettext-runtime-0.12.1.bin.woe32.zip:    ftp://ftp.gnu.org/gnu/gettext/gettext-runtime-0.12.1.bin.woe32.zip
+.. _gettext-tools-0.12.1.bin.woe32.zip:      ftp://ftp.gnu.org/gnu/gettext/gettext-tools-0.12.1.bin.woe32.zip
+.. _libiconv-1.9.1.bin.woe32.zip:            http://www.ibiblio.org/pub/gnu/libiconv/libiconv-1.9.1.bin.woe32.zip
+
+"""
+# -----------------------------------------------------------------------------
+# Module Import
+# -------------
+#
+import os
+import sys
+import wx
+import re
+
+# -----------------------------------------------------------------------------
+# Global variables
+# ----------------
+#
+
+__author__ = "Pierre Rouleau"
+__version__= "$Revision: 1.5 $"
+
+# -----------------------------------------------------------------------------
+
+def getlanguageDict():
+    languageDict = {}
+
+    if wx.VERSION >= (3, 0, 0):
+        app = wx.App()
+    else:
+        app = wx.PySimpleApp()
+
+    for lang in [x for x in dir(wx) if x.startswith("LANGUAGE")]:
+        i = wx.Locale(wx.LANGUAGE_DEFAULT).GetLanguageInfo(getattr(wx, lang))
+        if i:
+            languageDict[i.CanonicalName] = i.Description
+
+    return languageDict
+
+
+
+def processCustomFiles(filein, fileout, regexp, prefix = ''):
+    appfil_file = open(filein, 'r')
+    messages_file = open(fileout, 'r')
+    messages = messages_file.read()
+    messages_file.close()
+    messages_file = open(fileout, 'a')
+    messages_file.write('\n')
+    messages_file.write('#: %s\n' % prefix)
+    messages_file.write('\n')
+
+    words_found = {}
+    for filepath in appfil_file.xreadlines():
+        code_file = open(filepath.strip(), 'r')
+        for match in regexp.finditer(code_file.read()):
+            word = match.group(1)
+            if not words_found.get(word, False) and messages.find("msgid \"%s\"\nmsgstr \"\"" % word) == -1:
+                words_found[word] = True
+                messages_file.write('\n')
+                messages_file.write("msgid \"%s\"\n"%word)
+                messages_file.write("msgstr \"\"\n")
+        code_file.close()
+
+    messages_file.close()
+    appfil_file.close()
+
+
+# -----------------------------------------------------------------------------
+# m a k e P O ( )         -- Build the Portable Object file for the application --
+# ^^^^^^^^^^^^^^^
+#
+def makePO(applicationDirectoryPath,  applicationDomain=None, verbose=0) :
+    """Build the Portable Object Template file for the application.
+
+    makePO builds the .pot file for the application stored inside
+    a specified directory by running xgettext for all application source
+    files.  It finds the name of all files by looking for a file called 'app.fil'.
+    If this file does not exists, makePo raises an IOError exception.
+    By default the application domain (the application
+    name) is the same as the directory name but it can be overridden by the
+    'applicationDomain' argument.
+
+    makePO always creates a new file called messages.pot.  If it finds files
+    of the form app_xx.po where 'app' is the application name and 'xx' is one
+    of the ISO 639 two-letter language codes, makePO resynchronizes those
+    files with the latest extracted strings (now contained in messages.pot).
+    This process updates all line location number in the language-specific
+    .po files and may also create new entries for translation (or comment out
+    some).  The .po file is not changed, instead a new file is created with
+    the .new extension appended to the name of the .po file.
+
+    By default the function does not display what it is doing.  Set the
+    verbose argument to 1 to force it to print its commands.
+    """
+
+    if applicationDomain is None:
+        applicationName = fileBaseOf(applicationDirectoryPath,withPath=0)
+    else:
+        applicationName = applicationDomain
+    currentDir = os.getcwd()
+    os.chdir(applicationDirectoryPath)
+    filelist = 'app.fil'
+    if not os.path.exists(filelist):
+        raise IOError(2,'No module file: ' % filelist)
+
+    fileout = 'messages.pot'
+    # Steps:
+    #  Use xgettext to parse all application modules
+    #  The following switches are used:
+    #
+    #   -s                          : sort output by string content (easier to use when we need to merge several .po files)
+    #   --files-from=app.fil        : The list of files is taken from the file: app.fil
+    #   --output=                   : specifies the name of the output file (using a .pot extension)
+    cmd = 'xgettext -s --no-wrap --language=Python --files-from=' + filelist + ' --output=' + fileout + ' --package-name ' + applicationName
+    if verbose: print cmd
+    os.system(cmd)
+
+    XSD_STRING_MODEL = re.compile("<xsd\:(?:element|attribute) name=\"([^\"]*)\"[^\>]*\>")
+    processCustomFiles(filelist, fileout, XSD_STRING_MODEL, 'Extra XSD strings')
+
+    XML_TC6_STRING_MODEL = re.compile("<documentation>\s*<xhtml\:p><!\[CDATA\[([^\]]*)\]\]></xhtml\:p>\s*</documentation>", re.MULTILINE | re.DOTALL)
+    processCustomFiles(filelist, fileout, XML_TC6_STRING_MODEL, 'Extra TC6 documentation strings')
+
+    # generate messages.po
+    cmd = 'msginit --no-wrap --no-translator -i %s -l en_US.UTF-8 -o messages.po' % (fileout)
+    if verbose: print cmd
+    os.system(cmd)
+
+    languageDict = getlanguageDict()
+
+    for langCode in languageDict.keys():
+        if langCode == 'en':
+            pass
+        else:
+            langPOfileName = "%s_%s.po" % (applicationName , langCode)
+            if os.path.exists(langPOfileName):
+                cmd = 'msgmerge -s --no-wrap "%s" %s > "%s.new"' % (langPOfileName, fileout, langPOfileName)
+                if verbose: print cmd
+                os.system(cmd)
+    os.chdir(currentDir)
+
+# -----------------------------------------------------------------------------
+# c a t P O ( )         -- Concatenate one or several PO files with the application domain files. --
+# ^^^^^^^^^^^^^
+#
+def catPO(applicationDirectoryPath, listOf_extraPo, applicationDomain=None, targetDir=None, verbose=0) :
+    """Concatenate one or several PO files with the application domain files.
+    """
+
+    if applicationDomain is None:
+        applicationName = fileBaseOf(applicationDirectoryPath,withPath=0)
+    else:
+        applicationName = applicationDomain
+    currentDir = os.getcwd()
+    os.chdir(applicationDirectoryPath)
+
+    languageDict = getlanguageDict()
+
+    for langCode in languageDict.keys():
+        if langCode == 'en':
+            pass
+        else:
+            langPOfileName = "%s_%s.po" % (applicationName , langCode)
+            if os.path.exists(langPOfileName):
+                fileList = ''
+                for fileName in listOf_extraPo:
+                    fileList += ("%s_%s.po " % (fileName,langCode))
+                cmd = "msgcat -s --no-wrap %s %s > %s.cat" % (langPOfileName, fileList, langPOfileName)
+                if verbose: print cmd
+                os.system(cmd)
+                if targetDir is None:
+                    pass
+                else:
+                    mo_targetDir = "%s/%s/LC_MESSAGES" % (targetDir,langCode)
+                    cmd = "msgfmt --output-file=%s/%s.mo %s_%s.po.cat" % (mo_targetDir,applicationName,applicationName,langCode)
+                    if verbose: print cmd
+                    os.system(cmd)
+    os.chdir(currentDir)
+
+# -----------------------------------------------------------------------------
+# m a k e M O ( )         -- Compile the Portable Object files into the Machine Object stored in the right location. --
+# ^^^^^^^^^^^^^^^
+#
+def makeMO(applicationDirectoryPath,targetDir='./locale',applicationDomain=None, verbose=0, forceEnglish=0) :
+    """Compile the Portable Object files into the Machine Object stored in the right location.
+
+    makeMO converts all translated language-specific PO files located inside
+    the  application directory into the binary .MO files stored inside the
+    LC_MESSAGES sub-directory for the found locale files.
+
+    makeMO searches for all files that have a name of the form 'app_xx.po'
+    inside the application directory specified by the first argument.  The
+    'app' is the application domain name (that can be specified by the
+    applicationDomain argument or is taken from the directory name). The 'xx'
+    corresponds to one of the ISO 639 two-letter language codes.
+
+    makeMo stores the resulting files inside a sub-directory of `targetDir`
+    called xx/LC_MESSAGES where 'xx' corresponds to the 2-letter language
+    code.
+    """
+    if targetDir is None:
+        targetDir = './locale'
+    if verbose:
+        print "Target directory for .mo files is: %s" % targetDir
+
+    if applicationDomain is None:
+        applicationName = fileBaseOf(applicationDirectoryPath,withPath=0)
+    else:
+        applicationName = applicationDomain
+    currentDir = os.getcwd()
+    os.chdir(applicationDirectoryPath)
+
+    languageDict = getlanguageDict()
+
+    for langCode in languageDict.keys():
+        if (langCode == 'en') and (forceEnglish==0):
+            pass
+        else:
+            langPOfileName = "%s_%s.po" % (applicationName , langCode)
+            if os.path.exists(langPOfileName):
+                mo_targetDir = "%s/%s/LC_MESSAGES" % (targetDir,langCode)
+                if not os.path.exists(mo_targetDir):
+                    mkdir(mo_targetDir)
+                cmd = 'msgfmt --output-file="%s/%s.mo" "%s_%s.po"' % (mo_targetDir,applicationName,applicationName,langCode)
+                if verbose: print cmd
+                os.system(cmd)
+    os.chdir(currentDir)
+
+# -----------------------------------------------------------------------------
+# p r i n t U s a g e         -- Displays how to use this script from the command line --
+# ^^^^^^^^^^^^^^^^^^^
+#
+def printUsage(errorMsg=None) :
+    """Displays how to use this script from the command line."""
+    print """
+    ##################################################################################
+    #   mki18n :   Make internationalization files.                                  #
+    #              Uses the GNU gettext system to create PO (Portable Object) files  #
+    #              from source code, coimpile PO into MO (Machine Object) files.     #
+    #              Supports C,C++,Python source files.                               #
+    #                                                                                #
+    #   Usage: mki18n {OPTION} [appDirPath]                                          #
+    #                                                                                #
+    #   Options:                                                                     #
+    #     -e               : When -m is used, forces English .mo file creation       #
+    #     -h               : prints this help                                        #
+    #     -m               : make MO from existing PO files                          #
+    #     -p               : make PO, update PO files: Creates a new messages.pot    #
+    #                        file. Creates a dom_xx.po.new for every existing        #
+    #                        language specific .po file. ('xx' stands for the ISO639 #
+    #                        two-letter language code and 'dom' stands for the       #
+    #                        application domain name).  mki18n requires that you     #
+    #                        write a 'app.fil' file  which contains the list of all  #
+    #                        source code to parse.                                   #
+    #     -v               : verbose (prints comments while running)                 #
+    #     --domain=appName : specifies the application domain name.  By default      #
+    #                        the directory name is used.                             #
+    #     --moTarget=dir : specifies the directory where .mo files are stored.       #
+    #                      If not specified, the target is './locale'                #
+    #                                                                                #
+    #   You must specify one of the -p or -m option to perform the work.  You can    #
+    #   specify the path of the target application.  If you leave it out mki18n      #
+    #   will use the current directory as the application main directory.            #
+    #                                                                                #
+    ##################################################################################"""
+    if errorMsg:
+        print "\n   ERROR: %s" % errorMsg
+
+# -----------------------------------------------------------------------------
+# f i l e B a s e O f ( )         -- Return base name of filename --
+# ^^^^^^^^^^^^^^^^^^^^^^^
+#
+def fileBaseOf(filename,withPath=0) :
+   """fileBaseOf(filename,withPath) ---> string
+
+   Return base name of filename.  The returned string never includes the extension.
+   Use os.path.basename() to return the basename with the extension.  The
+   second argument is optional.  If specified and if set to 'true' (non zero)
+   the string returned contains the full path of the file name.  Otherwise the
+   path is excluded.
+
+   [Example]
+   >>> fn = 'd:/dev/telepath/tvapp/code/test.html'
+   >>> fileBaseOf(fn)
+   'test'
+   >>> fileBaseOf(fn)
+   'test'
+   >>> fileBaseOf(fn,1)
+   'd:/dev/telepath/tvapp/code/test'
+   >>> fileBaseOf(fn,0)
+   'test'
+   >>> fn = 'abcdef'
+   >>> fileBaseOf(fn)
+   'abcdef'
+   >>> fileBaseOf(fn,1)
+   'abcdef'
+   >>> fn = "abcdef."
+   >>> fileBaseOf(fn)
+   'abcdef'
+   >>> fileBaseOf(fn,1)
+   'abcdef'
+   """
+   pos = filename.rfind('.')
+   if pos > 0:
+      filename = filename[:pos]
+   if withPath:
+      return filename
+   else:
+      return os.path.basename(filename)
+# -----------------------------------------------------------------------------
+# m k d i r ( )         -- Create a directory (and possibly the entire tree) --
+# ^^^^^^^^^^^^^
+#
+def mkdir(directory) :
+   """Create a directory (and possibly the entire tree).
+
+   The os.mkdir() will fail to create a directory if one of the
+   directory in the specified path does not exist.  mkdir()
+   solves this problem.  It creates every intermediate directory
+   required to create the final path. Under Unix, the function
+   only supports forward slash separator, but under Windows and MacOS
+   the function supports the forward slash and the OS separator (backslash
+   under windows).
+   """
+
+   # translate the path separators
+   directory = unixpath(directory)
+   # build a list of all directory elements
+   aList = filter(lambda x: len(x)>0, directory.split('/'))
+   theLen = len(aList)
+   # if the first element is a Windows-style disk drive
+   # concatenate it with the first directory
+   if aList[0].endswith(':'):
+      if theLen > 1:
+         aList[1] = aList[0] + '/' + aList[1]
+         del aList[0]
+         theLen -= 1
+   # if the original directory starts at root,
+   # make sure the first element of the list
+   # starts at root too
+   if directory[0] == '/':
+      aList[0] = '/' + aList[0]
+   # Now iterate through the list, check if the
+   # directory exists and if not create it
+   theDir = ''
+   for i in range(theLen):
+      theDir += aList[i]
+      if not os.path.exists(theDir):
+         os.mkdir(theDir)
+      theDir += '/'
+
+# -----------------------------------------------------------------------------
+# u n i x p a t h ( )         -- Return a path name that contains Unix separator. --
+# ^^^^^^^^^^^^^^^^^^^
+#
+def unixpath(thePath) :
+   r"""Return a path name that contains Unix separator.
+
+   [Example]
+   >>> unixpath(r"d:\test")
+   'd:/test'
+   >>> unixpath("d:/test/file.txt")
+   'd:/test/file.txt'
+   >>>
+   """
+   thePath = os.path.normpath(thePath)
+   if os.sep == '/':
+      return thePath
+   else:
+      return thePath.replace(os.sep,'/')
+
+# -----------------------------------------------------------------------------
+
+# S c r i p t   e x e c u t i o n               -- Runs when invoked from the command line --
+# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+#
+if __name__ == "__main__":
+    import getopt     # command line parsing
+    argc = len(sys.argv)
+    if argc == 1:
+        printUsage('Missing argument: specify at least one of -m or -p (or both).')
+        sys.exit(1)
+    # If there is some arguments, parse the command line
+    validOptions     = "ehmpv"
+    validLongOptions = ['domain=', 'moTarget=']
+    option = {}
+    option['forceEnglish'] = 0
+    option['mo'] = 0
+    option['po'] = 0
+    option['verbose'] = 0
+    option['domain'] = None
+    option['moTarget'] = None
+    try:
+        optionList,pargs = getopt.getopt(sys.argv[1:],validOptions,validLongOptions)
+    except getopt.GetoptError, e:
+        printUsage(e[0])
+        sys.exit(1)
+    for (opt,val) in optionList:
+        if  (opt == '-h'):
+            printUsage()
+            sys.exit(0)
+        elif (opt == '-e'):         option['forceEnglish'] = 1
+        elif (opt == '-m'):         option['mo'] = 1
+        elif (opt == '-p'):         option['po'] = 1
+        elif (opt == '-v'):         option['verbose'] = 1
+        elif (opt == '--domain'):   option['domain'] = val
+        elif (opt == '--moTarget'): option['moTarget'] = val
+    if len(pargs) == 0:
+        appDirPath = os.getcwd()
+        if option['verbose']:
+            print "No project directory given. Using current one:  %s" % appDirPath
+    elif len(pargs) == 1:
+        appDirPath = pargs[0]
+    else:
+        printUsage('Too many arguments (%u).  Use double quotes if you have space in directory name' % len(pargs))
+        sys.exit(1)
+    if option['domain'] is None:
+        # If no domain specified, use the name of the target directory
+        option['domain'] = fileBaseOf(appDirPath)
+    if option['verbose']:
+        print "Application domain used is: '%s'" % option['domain']
+    if option['po']:
+        try:
+            makePO(appDirPath,option['domain'],option['verbose'])
+        except IOError, e:
+            printUsage(e[1] + '\n   You must write a file app.fil that contains the list of all files to parse.')
+    if option['mo']:
+        makeMO(appDirPath,option['moTarget'],option['domain'],option['verbose'],option['forceEnglish'])
+    sys.exit(1)
+
+
+# -----------------------------------------------------------------------------