i18n/mki18n.py
changeset 1784 64beb9e9c749
parent 1758 845ca626db09
child 1826 91796f408540
equal deleted inserted replaced
1729:31e63e25b4cc 1784:64beb9e9c749
     1 #! /usr/bin/env python
     1 #! /usr/bin/env python
     2 # -*- coding: iso-8859-1 -*-
     2 # -*- coding: iso-8859-1 -*-
     3 # 
     3 #
     4 #   PYTHON MODULE:     MKI18N.PY
     4 #   PYTHON MODULE:     MKI18N.PY
     5 #                      =========
     5 #                      =========
     6 # 
     6 #
     7 #   Abstract:         Make Internationalization (i18n) files for an application.
     7 #   Abstract:         Make Internationalization (i18n) files for an application.
     8 # 
     8 #
     9 #   Copyright Pierre Rouleau. 2003. Released to public domain.
     9 #   Copyright Pierre Rouleau. 2003. Released to public domain.
    10 # 
    10 #
    11 #   Last update: Saturday, November 8, 2003. @ 15:55:18.
    11 #   Last update: Saturday, November 8, 2003. @ 15:55:18.
    12 # 
    12 #
    13 #   File: ROUP2003N01::C:/dev/python/mki18n.py
    13 #   File: ROUP2003N01::C:/dev/python/mki18n.py
    14 # 
    14 #
    15 #   RCS $Header: //software/official/MKS/MKS_SI/TV_NT/dev/Python/rcs/mki18n.py 1.5 2003/11/05 19:40:04 PRouleau Exp $
    15 #   RCS $Header: //software/official/MKS/MKS_SI/TV_NT/dev/Python/rcs/mki18n.py 1.5 2003/11/05 19:40:04 PRouleau Exp $
    16 # 
    16 #
    17 #   Update history:
    17 #   Update history:
    18 # 
    18 #
    19 #   - File created: Saturday, June 7, 2003. by Pierre Rouleau
    19 #   - File created: Saturday, June 7, 2003. by Pierre Rouleau
    20 #   - 10/06/03 rcs : RCS Revision 1.1  2003/06/10 10:06:12  PRouleau
    20 #   - 10/06/03 rcs : RCS Revision 1.1  2003/06/10 10:06:12  PRouleau
    21 #   - 10/06/03 rcs : RCS Initial revision
    21 #   - 10/06/03 rcs : RCS Initial revision
    22 #   - 23/08/03 rcs : RCS Revision 1.2  2003/06/10 10:54:27  PRouleau
    22 #   - 23/08/03 rcs : RCS Revision 1.2  2003/06/10 10:54:27  PRouleau
    23 #   - 23/08/03 P.R.: [code:fix] : The strings encoded in this file are encode in iso-8859-1 format.  Added the encoding
    23 #   - 23/08/03 P.R.: [code:fix] : The strings encoded in this file are encode in iso-8859-1 format.  Added the encoding
    25 #   - 23/08/03 P.R.: [feature:new] : Added the '-e' switch which is used to force the creation of the empty English .mo file.
    25 #   - 23/08/03 P.R.: [feature:new] : Added the '-e' switch which is used to force the creation of the empty English .mo file.
    26 #   - 22/10/03 P.R.: [code] : incorporated utility functions in here to make script self sufficient.
    26 #   - 22/10/03 P.R.: [code] : incorporated utility functions in here to make script self sufficient.
    27 #   - 05/11/03 rcs : RCS Revision 1.4  2003/10/22 06:39:31  PRouleau
    27 #   - 05/11/03 rcs : RCS Revision 1.4  2003/10/22 06:39:31  PRouleau
    28 #   - 05/11/03 P.R.: [code:fix] : included the unixpath() in this file.
    28 #   - 05/11/03 P.R.: [code:fix] : included the unixpath() in this file.
    29 #   - 08/11/03 rcs : RCS Revision 1.5  2003/11/05 19:40:04  PRouleau
    29 #   - 08/11/03 rcs : RCS Revision 1.5  2003/11/05 19:40:04  PRouleau
    30 # 
    30 #
    31 #   RCS $Log: $
    31 #   RCS $Log: $
    32 # 
    32 #
    33 # 
    33 #
    34 # -----------------------------------------------------------------------------
    34 # -----------------------------------------------------------------------------
    35 """                                
    35 """
    36 mki18n allows you to internationalize your software.  You can use it to 
    36 mki18n allows you to internationalize your software.  You can use it to
    37 create the GNU .po files (Portable Object) and the compiled .mo files
    37 create the GNU .po files (Portable Object) and the compiled .mo files
    38 (Machine Object).
    38 (Machine Object).
    39 
    39 
    40 mki18n module can be used from the command line or from within a script (see 
    40 mki18n module can be used from the command line or from within a script (see
    41 the Usage at the end of this page).
    41 the Usage at the end of this page).
    42 
    42 
    43     Table of Contents
    43     Table of Contents
    44     -----------------
    44     -----------------
    45     
    45 
    46     makePO()             -- Build the Portable Object file for the application --
    46     makePO()             -- Build the Portable Object file for the application --
    47     catPO()              -- Concatenate one or several PO files with the application domain files. --
    47     catPO()              -- Concatenate one or several PO files with the application domain files. --
    48     makeMO()             -- Compile the Portable Object files into the Machine Object stored in the right location. --
    48     makeMO()             -- Compile the Portable Object files into the Machine Object stored in the right location. --
    49     printUsage           -- Displays how to use this script from the command line --
    49     printUsage           -- Displays how to use this script from the command line --
    50 
    50 
    58    - `GNU FTP site for gettetx`_ where several versions (0.10.40, 0.11.2, 0.11.5 and 0.12.1) are available.
    58    - `GNU FTP site for gettetx`_ where several versions (0.10.40, 0.11.2, 0.11.5 and 0.12.1) are available.
    59      Note  that you need to use `GNU libiconv`_ to use this. Get it from the `GNU
    59      Note  that you need to use `GNU libiconv`_ to use this. Get it from the `GNU
    60      libiconv  ftp site`_ and get version 1.9.1 or later. Get the Windows .ZIP
    60      libiconv  ftp site`_ and get version 1.9.1 or later. Get the Windows .ZIP
    61      files and install the packages inside c:/gnu. All binaries will be stored
    61      files and install the packages inside c:/gnu. All binaries will be stored
    62      inside  c:/gnu/bin.  Just  put c:/gnu/bin inside your PATH. You will need
    62      inside  c:/gnu/bin.  Just  put c:/gnu/bin inside your PATH. You will need
    63      the following files: 
    63      the following files:
    64 
    64 
    65       - `gettext-runtime-0.12.1.bin.woe32.zip`_ 
    65       - `gettext-runtime-0.12.1.bin.woe32.zip`_
    66       - `gettext-tools-0.12.1.bin.woe32.zip`_
    66       - `gettext-tools-0.12.1.bin.woe32.zip`_
    67       - `libiconv-1.9.1.bin.woe32.zip`_ 
    67       - `libiconv-1.9.1.bin.woe32.zip`_
    68 
    68 
    69 
    69 
    70 .. _GNU libiconv:                            http://www.gnu.org/software/libiconv/
    70 .. _GNU libiconv:                            http://www.gnu.org/software/libiconv/
    71 .. _GNU libiconv ftp site:                   http://www.ibiblio.org/pub/gnu/libiconv/
    71 .. _GNU libiconv ftp site:                   http://www.ibiblio.org/pub/gnu/libiconv/
    72 .. _gettext-runtime-0.12.1.bin.woe32.zip:    ftp://ftp.gnu.org/gnu/gettext/gettext-runtime-0.12.1.bin.woe32.zip           
    72 .. _gettext-runtime-0.12.1.bin.woe32.zip:    ftp://ftp.gnu.org/gnu/gettext/gettext-runtime-0.12.1.bin.woe32.zip
    73 .. _gettext-tools-0.12.1.bin.woe32.zip:      ftp://ftp.gnu.org/gnu/gettext/gettext-tools-0.12.1.bin.woe32.zip 
    73 .. _gettext-tools-0.12.1.bin.woe32.zip:      ftp://ftp.gnu.org/gnu/gettext/gettext-tools-0.12.1.bin.woe32.zip
    74 .. _libiconv-1.9.1.bin.woe32.zip:            http://www.ibiblio.org/pub/gnu/libiconv/libiconv-1.9.1.bin.woe32.zip
    74 .. _libiconv-1.9.1.bin.woe32.zip:            http://www.ibiblio.org/pub/gnu/libiconv/libiconv-1.9.1.bin.woe32.zip
    75 
    75 
    76 """
    76 """
    77 # -----------------------------------------------------------------------------
    77 # -----------------------------------------------------------------------------
    78 # Module Import
    78 # Module Import
    79 # -------------
    79 # -------------
    80 # 
    80 #
    81 import os
    81 import os
    82 import sys
    82 import sys
    83 import wx
    83 import wx
    84 import re
    84 import re
    85 
    85 
    87 # Global variables
    87 # Global variables
    88 # ----------------
    88 # ----------------
    89 #
    89 #
    90 
    90 
    91 __author__ = "Pierre Rouleau"
    91 __author__ = "Pierre Rouleau"
    92 __version__= "$Revision: 1.5 $"
    92 __version__ = "$Revision: 1.5 $"
    93 
    93 
    94 # -----------------------------------------------------------------------------
    94 # -----------------------------------------------------------------------------
       
    95 
    95 
    96 
    96 def getlanguageDict():
    97 def getlanguageDict():
    97     languageDict = {}
    98     languageDict = {}
    98 
    99 
    99     if wx.VERSION >= (3, 0, 0):
   100     if wx.VERSION >= (3, 0, 0):
   107             languageDict[i.CanonicalName] = i.Description
   108             languageDict[i.CanonicalName] = i.Description
   108 
   109 
   109     return languageDict
   110     return languageDict
   110 
   111 
   111 
   112 
   112 
   113 def verbosePrint(verbose, str):
   113 def processCustomFiles(filein, fileout, regexp, prefix = ''):
   114     if verbose:
       
   115         print str
       
   116 
       
   117 
       
   118 def processCustomFiles(filein, fileout, regexp, prefix=''):
   114     appfil_file = open(filein, 'r')
   119     appfil_file = open(filein, 'r')
   115     messages_file = open(fileout, 'r')
   120     messages_file = open(fileout, 'r')
   116     messages = messages_file.read()
   121     messages = messages_file.read()
   117     messages_file.close()
   122     messages_file.close()
   118     messages_file = open(fileout, 'a')
   123     messages_file = open(fileout, 'a')
   126         for match in regexp.finditer(code_file.read()):
   131         for match in regexp.finditer(code_file.read()):
   127             word = match.group(1)
   132             word = match.group(1)
   128             if not words_found.get(word, False) and messages.find("msgid \"%s\"\nmsgstr \"\"" % word) == -1:
   133             if not words_found.get(word, False) and messages.find("msgid \"%s\"\nmsgstr \"\"" % word) == -1:
   129                 words_found[word] = True
   134                 words_found[word] = True
   130                 messages_file.write('\n')
   135                 messages_file.write('\n')
   131                 messages_file.write("msgid \"%s\"\n"%word)
   136                 messages_file.write("msgid \"%s\"\n" % word)
   132                 messages_file.write("msgstr \"\"\n")
   137                 messages_file.write("msgstr \"\"\n")
   133         code_file.close()
   138         code_file.close()
   134 
   139 
   135     messages_file.close()
   140     messages_file.close()
   136     appfil_file.close()
   141     appfil_file.close()
   138 
   143 
   139 # -----------------------------------------------------------------------------
   144 # -----------------------------------------------------------------------------
   140 # m a k e P O ( )         -- Build the Portable Object file for the application --
   145 # m a k e P O ( )         -- Build the Portable Object file for the application --
   141 # ^^^^^^^^^^^^^^^
   146 # ^^^^^^^^^^^^^^^
   142 #
   147 #
   143 def makePO(applicationDirectoryPath,  applicationDomain=None, verbose=0) :
   148 def makePO(applicationDirectoryPath,  applicationDomain=None, verbose=0):
   144     """Build the Portable Object Template file for the application.
   149     """Build the Portable Object Template file for the application.
   145 
   150 
   146     makePO builds the .pot file for the application stored inside 
   151     makePO builds the .pot file for the application stored inside
   147     a specified directory by running xgettext for all application source 
   152     a specified directory by running xgettext for all application source
   148     files.  It finds the name of all files by looking for a file called 'app.fil'. 
   153     files.  It finds the name of all files by looking for a file called 'app.fil'.
   149     If this file does not exists, makePo raises an IOError exception.
   154     If this file does not exists, makePo raises an IOError exception.
   150     By default the application domain (the application
   155     By default the application domain (the application
   151     name) is the same as the directory name but it can be overridden by the
   156     name) is the same as the directory name but it can be overridden by the
   152     'applicationDomain' argument.
   157     'applicationDomain' argument.
   153 
   158 
   154     makePO always creates a new file called messages.pot.  If it finds files 
   159     makePO always creates a new file called messages.pot.  If it finds files
   155     of the form app_xx.po where 'app' is the application name and 'xx' is one 
   160     of the form app_xx.po where 'app' is the application name and 'xx' is one
   156     of the ISO 639 two-letter language codes, makePO resynchronizes those 
   161     of the ISO 639 two-letter language codes, makePO resynchronizes those
   157     files with the latest extracted strings (now contained in messages.pot). 
   162     files with the latest extracted strings (now contained in messages.pot).
   158     This process updates all line location number in the language-specific
   163     This process updates all line location number in the language-specific
   159     .po files and may also create new entries for translation (or comment out 
   164     .po files and may also create new entries for translation (or comment out
   160     some).  The .po file is not changed, instead a new file is created with 
   165     some).  The .po file is not changed, instead a new file is created with
   161     the .new extension appended to the name of the .po file.
   166     the .new extension appended to the name of the .po file.
   162 
   167 
   163     By default the function does not display what it is doing.  Set the 
   168     By default the function does not display what it is doing.  Set the
   164     verbose argument to 1 to force it to print its commands.
   169     verbose argument to 1 to force it to print its commands.
   165     """
   170     """
   166 
   171 
   167     if applicationDomain is None:
   172     if applicationDomain is None:
   168         applicationName = fileBaseOf(applicationDirectoryPath,withPath=0)
   173         applicationName = fileBaseOf(applicationDirectoryPath, withPath=0)
   169     else:
   174     else:
   170         applicationName = applicationDomain
   175         applicationName = applicationDomain
   171     currentDir = os.getcwd()
   176     currentDir = os.getcwd()
   172     os.chdir(applicationDirectoryPath)
   177     os.chdir(applicationDirectoryPath)
   173     filelist = 'app.fil'
   178     filelist = 'app.fil'
   174     if not os.path.exists(filelist):
   179     if not os.path.exists(filelist):
   175         raise IOError(2,'No module file: ' % filelist)
   180         raise IOError(2, 'No module file: ' % filelist)
   176 
   181 
   177     fileout = 'messages.pot'
   182     fileout = 'messages.pot'
   178     # Steps:                                  
   183     # Steps:
   179     #  Use xgettext to parse all application modules
   184     #  Use xgettext to parse all application modules
   180     #  The following switches are used:
   185     #  The following switches are used:
   181     #  
   186     #
   182     #   -s                          : sort output by string content (easier to use when we need to merge several .po files)
   187     #   -s                          : sort output by string content (easier to use when we need to merge several .po files)
   183     #   --files-from=app.fil        : The list of files is taken from the file: app.fil
   188     #   --files-from=app.fil        : The list of files is taken from the file: app.fil
   184     #   --output=                   : specifies the name of the output file (using a .pot extension)
   189     #   --output=                   : specifies the name of the output file (using a .pot extension)
   185     cmd = 'xgettext -s --no-wrap --language=Python --files-from=' + filelist + ' --output=' + fileout + ' --package-name ' + applicationName
   190     cmd = 'xgettext -s --no-wrap --language=Python --files-from=' + filelist + ' --output=' + fileout + ' --package-name ' + applicationName
   186     if verbose: print cmd
   191     verbosePrint(verbose, cmd)
   187     os.system(cmd)
   192     os.system(cmd)
   188 
   193 
   189     XSD_STRING_MODEL = re.compile("<xsd\:(?:element|attribute) name=\"([^\"]*)\"[^\>]*\>")
   194     XSD_STRING_MODEL = re.compile("<xsd\:(?:element|attribute) name=\"([^\"]*)\"[^\>]*\>")
   190     processCustomFiles(filelist, fileout, XSD_STRING_MODEL, 'Extra XSD strings')
   195     processCustomFiles(filelist, fileout, XSD_STRING_MODEL, 'Extra XSD strings')
   191 
   196 
   192     XML_TC6_STRING_MODEL = re.compile("<documentation>\s*<xhtml\:p><!\[CDATA\[([^\]]*)\]\]></xhtml\:p>\s*</documentation>", re.MULTILINE | re.DOTALL)
   197     XML_TC6_STRING_MODEL = re.compile("<documentation>\s*<xhtml\:p><!\[CDATA\[([^\]]*)\]\]></xhtml\:p>\s*</documentation>", re.MULTILINE | re.DOTALL)
   193     processCustomFiles(filelist, fileout, XML_TC6_STRING_MODEL, 'Extra TC6 documentation strings')    
   198     processCustomFiles(filelist, fileout, XML_TC6_STRING_MODEL, 'Extra TC6 documentation strings')
   194 
   199 
   195     # generate messages.po
   200     # generate messages.po
   196     cmd = 'msginit --no-wrap --no-translator -i %s -l en_US.UTF-8 -o messages.po' % (fileout)
   201     cmd = 'msginit --no-wrap --no-translator -i %s -l en_US.UTF-8 -o messages.po' % (fileout)
   197     if verbose: print cmd
   202     verbosePrint(verbose, cmd)
   198     os.system(cmd)    
   203     os.system(cmd)
   199 
   204 
   200     languageDict = getlanguageDict()
   205     languageDict = getlanguageDict()
   201 
   206 
   202     for langCode in languageDict.keys():
   207     for langCode in languageDict.keys():
   203         if langCode == 'en':
   208         if langCode == 'en':
   204             pass
   209             pass
   205         else:
   210         else:
   206             langPOfileName = "%s_%s.po" % (applicationName , langCode)
   211             langPOfileName = "%s_%s.po" % (applicationName, langCode)
   207             if os.path.exists(langPOfileName):
   212             if os.path.exists(langPOfileName):
   208                 cmd = 'msgmerge -s --no-wrap "%s" %s > "%s.new"' % (langPOfileName, fileout, langPOfileName)
   213                 cmd = 'msgmerge -s --no-wrap "%s" %s > "%s.new"' % (langPOfileName, fileout, langPOfileName)
   209                 if verbose: print cmd
   214                 verbosePrint(verbose, cmd)
   210                 os.system(cmd)
   215                 os.system(cmd)
   211     os.chdir(currentDir)
   216     os.chdir(currentDir)
   212 
   217 
   213 # -----------------------------------------------------------------------------
   218 
   214 # c a t P O ( )         -- Concatenate one or several PO files with the application domain files. --
   219 def catPO(applicationDirectoryPath, listOf_extraPo, applicationDomain=None, targetDir=None, verbose=0):
   215 # ^^^^^^^^^^^^^
       
   216 #
       
   217 def catPO(applicationDirectoryPath, listOf_extraPo, applicationDomain=None, targetDir=None, verbose=0) :
       
   218     """Concatenate one or several PO files with the application domain files.
   220     """Concatenate one or several PO files with the application domain files.
   219     """
   221     """
   220 
   222 
   221     if applicationDomain is None:
   223     if applicationDomain is None:
   222         applicationName = fileBaseOf(applicationDirectoryPath,withPath=0)
   224         applicationName = fileBaseOf(applicationDirectoryPath, withPath=0)
   223     else:
   225     else:
   224         applicationName = applicationDomain
   226         applicationName = applicationDomain
   225     currentDir = os.getcwd()
   227     currentDir = os.getcwd()
   226     os.chdir(applicationDirectoryPath)
   228     os.chdir(applicationDirectoryPath)
   227 
   229 
   229 
   231 
   230     for langCode in languageDict.keys():
   232     for langCode in languageDict.keys():
   231         if langCode == 'en':
   233         if langCode == 'en':
   232             pass
   234             pass
   233         else:
   235         else:
   234             langPOfileName = "%s_%s.po" % (applicationName , langCode)
   236             langPOfileName = "%s_%s.po" % (applicationName, langCode)
   235             if os.path.exists(langPOfileName):
   237             if os.path.exists(langPOfileName):
   236                 fileList = ''
   238                 fileList = ''
   237                 for fileName in listOf_extraPo:
   239                 for fileName in listOf_extraPo:
   238                     fileList += ("%s_%s.po " % (fileName,langCode))
   240                     fileList += ("%s_%s.po " % (fileName, langCode))
   239                 cmd = "msgcat -s --no-wrap %s %s > %s.cat" % (langPOfileName, fileList, langPOfileName)
   241                 cmd = "msgcat -s --no-wrap %s %s > %s.cat" % (langPOfileName, fileList, langPOfileName)
   240                 if verbose: print cmd
   242                 verbosePrint(verbose, cmd)
   241                 os.system(cmd)
   243                 os.system(cmd)
   242                 if targetDir is None:
   244                 if targetDir is None:
   243                     pass
   245                     pass
   244                 else:
   246                 else:
   245                     mo_targetDir = "%s/%s/LC_MESSAGES" % (targetDir,langCode)
   247                     mo_targetDir = "%s/%s/LC_MESSAGES" % (targetDir, langCode)
   246                     cmd = "msgfmt --output-file=%s/%s.mo %s_%s.po.cat" % (mo_targetDir,applicationName,applicationName,langCode)
   248                     cmd = "msgfmt --output-file=%s/%s.mo %s_%s.po.cat" % (mo_targetDir, applicationName, applicationName, langCode)
   247                     if verbose: print cmd
   249                     verbosePrint(verbose, cmd)
   248                     os.system(cmd)
   250                     os.system(cmd)
   249     os.chdir(currentDir)
   251     os.chdir(currentDir)
   250 
   252 
   251 # -----------------------------------------------------------------------------
   253 
   252 # m a k e M O ( )         -- Compile the Portable Object files into the Machine Object stored in the right location. --
   254 def makeMO(applicationDirectoryPath, targetDir='./locale', applicationDomain=None, verbose=0, forceEnglish=0):
   253 # ^^^^^^^^^^^^^^^
       
   254 # 
       
   255 def makeMO(applicationDirectoryPath,targetDir='./locale',applicationDomain=None, verbose=0, forceEnglish=0) :
       
   256     """Compile the Portable Object files into the Machine Object stored in the right location.
   255     """Compile the Portable Object files into the Machine Object stored in the right location.
   257 
   256 
   258     makeMO converts all translated language-specific PO files located inside 
   257     makeMO converts all translated language-specific PO files located inside
   259     the  application directory into the binary .MO files stored inside the 
   258     the  application directory into the binary .MO files stored inside the
   260     LC_MESSAGES sub-directory for the found locale files.
   259     LC_MESSAGES sub-directory for the found locale files.
   261 
   260 
   262     makeMO searches for all files that have a name of the form 'app_xx.po' 
   261     makeMO searches for all files that have a name of the form 'app_xx.po'
   263     inside the application directory specified by the first argument.  The 
   262     inside the application directory specified by the first argument.  The
   264     'app' is the application domain name (that can be specified by the 
   263     'app' is the application domain name (that can be specified by the
   265     applicationDomain argument or is taken from the directory name). The 'xx' 
   264     applicationDomain argument or is taken from the directory name). The 'xx'
   266     corresponds to one of the ISO 639 two-letter language codes.
   265     corresponds to one of the ISO 639 two-letter language codes.
   267 
   266 
   268     makeMo stores the resulting files inside a sub-directory of `targetDir`
   267     makeMo stores the resulting files inside a sub-directory of `targetDir`
   269     called xx/LC_MESSAGES where 'xx' corresponds to the 2-letter language
   268     called xx/LC_MESSAGES where 'xx' corresponds to the 2-letter language
   270     code.
   269     code.
   271     """
   270     """
       
   271 
   272     if targetDir is None:
   272     if targetDir is None:
   273         targetDir = './locale'
   273         targetDir = './locale'
   274     if verbose:
   274 
   275         print "Target directory for .mo files is: %s" % targetDir
   275     verbosePrint(verbose, "Target directory for .mo files is: %s" % targetDir)
   276 
   276 
   277     if applicationDomain is None:
   277     if applicationDomain is None:
   278         applicationName = fileBaseOf(applicationDirectoryPath,withPath=0)
   278         applicationName = fileBaseOf(applicationDirectoryPath, withPath=0)
   279     else:
   279     else:
   280         applicationName = applicationDomain
   280         applicationName = applicationDomain
   281     currentDir = os.getcwd()
   281     currentDir = os.getcwd()
   282     os.chdir(applicationDirectoryPath)
   282     os.chdir(applicationDirectoryPath)
   283 
   283 
   284     languageDict = getlanguageDict()
   284     languageDict = getlanguageDict()
   285 
   285 
   286     for langCode in languageDict.keys():
   286     for langCode in languageDict.keys():
   287         if (langCode == 'en') and (forceEnglish==0):
   287         if (langCode == 'en') and (forceEnglish == 0):
   288             pass
   288             pass
   289         else:
   289         else:
   290             langPOfileName = "%s_%s.po" % (applicationName , langCode)
   290             langPOfileName = "%s_%s.po" % (applicationName, langCode)
   291             if os.path.exists(langPOfileName):
   291             if os.path.exists(langPOfileName):
   292                 mo_targetDir = "%s/%s/LC_MESSAGES" % (targetDir,langCode)
   292                 mo_targetDir = "%s/%s/LC_MESSAGES" % (targetDir, langCode)
   293                 if not os.path.exists(mo_targetDir):
   293                 if not os.path.exists(mo_targetDir):
   294                     mkdir(mo_targetDir)
   294                     mkdir(mo_targetDir)
   295                 cmd = 'msgfmt --output-file="%s/%s.mo" "%s_%s.po"' % (mo_targetDir,applicationName,applicationName,langCode)
   295                 cmd = 'msgfmt --output-file="%s/%s.mo" "%s_%s.po"' % (mo_targetDir, applicationName, applicationName, langCode)
   296                 if verbose: print cmd
   296                 verbosePrint(verbose, cmd)
   297                 os.system(cmd)
   297                 os.system(cmd)
   298     os.chdir(currentDir)
   298     os.chdir(currentDir)
   299    
   299 
   300 # -----------------------------------------------------------------------------
   300 
   301 # p r i n t U s a g e         -- Displays how to use this script from the command line --
   301 def printUsage(errorMsg=None):
   302 # ^^^^^^^^^^^^^^^^^^^
       
   303 #
       
   304 def printUsage(errorMsg=None) :
       
   305     """Displays how to use this script from the command line."""
   302     """Displays how to use this script from the command line."""
   306     print """
   303     print """
   307     ##################################################################################
   304     ##################################################################################
   308     #   mki18n :   Make internationalization files.                                  #
   305     #   mki18n :   Make internationalization files.                                  #
   309     #              Uses the GNU gettext system to create PO (Portable Object) files  #
   306     #              Uses the GNU gettext system to create PO (Portable Object) files  #
   329     #     --moTarget=dir : specifies the directory where .mo files are stored.       #
   326     #     --moTarget=dir : specifies the directory where .mo files are stored.       #
   330     #                      If not specified, the target is './locale'                #
   327     #                      If not specified, the target is './locale'                #
   331     #                                                                                #
   328     #                                                                                #
   332     #   You must specify one of the -p or -m option to perform the work.  You can    #
   329     #   You must specify one of the -p or -m option to perform the work.  You can    #
   333     #   specify the path of the target application.  If you leave it out mki18n      #
   330     #   specify the path of the target application.  If you leave it out mki18n      #
   334     #   will use the current directory as the application main directory.            #        
   331     #   will use the current directory as the application main directory.            #
   335     #                                                                                #
   332     #                                                                                #
   336     ##################################################################################"""
   333     ##################################################################################"""
   337     if errorMsg:
   334     if errorMsg:
   338         print "\n   ERROR: %s" % errorMsg
   335         print "\n   ERROR: %s" % errorMsg
   339 
   336 
   340 # -----------------------------------------------------------------------------
   337 
   341 # f i l e B a s e O f ( )         -- Return base name of filename --
   338 def fileBaseOf(filename, withPath=0):
   342 # ^^^^^^^^^^^^^^^^^^^^^^^
   339     """fileBaseOf(filename,withPath) ---> string
   343 # 
   340 
   344 def fileBaseOf(filename,withPath=0) :
   341     Return base name of filename.  The returned string never includes the extension.
   345    """fileBaseOf(filename,withPath) ---> string
   342     Use os.path.basename() to return the basename with the extension.  The
   346 
   343     second argument is optional.  If specified and if set to 'true' (non zero)
   347    Return base name of filename.  The returned string never includes the extension.
   344     the string returned contains the full path of the file name.  Otherwise the
   348    Use os.path.basename() to return the basename with the extension.  The 
   345     path is excluded.
   349    second argument is optional.  If specified and if set to 'true' (non zero) 
   346 
   350    the string returned contains the full path of the file name.  Otherwise the 
   347     [Example]
   351    path is excluded.
   348     >>> fn = 'd:/dev/telepath/tvapp/code/test.html'
   352 
   349     >>> fileBaseOf(fn)
   353    [Example]
   350     'test'
   354    >>> fn = 'd:/dev/telepath/tvapp/code/test.html'
   351     >>> fileBaseOf(fn)
   355    >>> fileBaseOf(fn)
   352     'test'
   356    'test'
   353     >>> fileBaseOf(fn,1)
   357    >>> fileBaseOf(fn)
   354     'd:/dev/telepath/tvapp/code/test'
   358    'test'
   355     >>> fileBaseOf(fn,0)
   359    >>> fileBaseOf(fn,1)
   356     'test'
   360    'd:/dev/telepath/tvapp/code/test'
   357     >>> fn = 'abcdef'
   361    >>> fileBaseOf(fn,0)
   358     >>> fileBaseOf(fn)
   362    'test'
   359     'abcdef'
   363    >>> fn = 'abcdef'
   360     >>> fileBaseOf(fn,1)
   364    >>> fileBaseOf(fn)
   361     'abcdef'
   365    'abcdef'
   362     >>> fn = "abcdef."
   366    >>> fileBaseOf(fn,1)
   363     >>> fileBaseOf(fn)
   367    'abcdef'
   364     'abcdef'
   368    >>> fn = "abcdef."
   365     >>> fileBaseOf(fn,1)
   369    >>> fileBaseOf(fn)
   366     'abcdef'
   370    'abcdef'
   367     """
   371    >>> fileBaseOf(fn,1)
   368     pos = filename.rfind('.')
   372    'abcdef'
   369     if pos > 0:
   373    """            
   370         filename = filename[:pos]
   374    pos = filename.rfind('.')             
   371     if withPath:
   375    if pos > 0:
   372         return filename
   376       filename = filename[:pos]
   373     else:
   377    if withPath:
   374         return os.path.basename(filename)
   378       return filename
   375 
   379    else:
   376 
   380       return os.path.basename(filename)
   377 def mkdir(directory):
   381 # -----------------------------------------------------------------------------
   378     """Create a directory (and possibly the entire tree).
   382 # m k d i r ( )         -- Create a directory (and possibly the entire tree) --
   379 
   383 # ^^^^^^^^^^^^^
   380     The os.mkdir() will fail to create a directory if one of the
   384 # 
   381     directory in the specified path does not exist.  mkdir()
   385 def mkdir(directory) :
   382     solves this problem.  It creates every intermediate directory
   386    """Create a directory (and possibly the entire tree).
   383     required to create the final path. Under Unix, the function
   387 
   384     only supports forward slash separator, but under Windows and MacOS
   388    The os.mkdir() will fail to create a directory if one of the
   385     the function supports the forward slash and the OS separator (backslash
   389    directory in the specified path does not exist.  mkdir()
   386     under windows).
   390    solves this problem.  It creates every intermediate directory
   387     """
   391    required to create the final path. Under Unix, the function 
   388 
   392    only supports forward slash separator, but under Windows and MacOS
   389     # translate the path separators
   393    the function supports the forward slash and the OS separator (backslash
   390     directory = unixpath(directory)
   394    under windows).
   391     # build a list of all directory elements
   395    """ 
   392     aList = filter(lambda x: len(x) > 0, directory.split('/'))
   396 
   393     theLen = len(aList)
   397    # translate the path separators
   394     # if the first element is a Windows-style disk drive
   398    directory = unixpath(directory)
   395     # concatenate it with the first directory
   399    # build a list of all directory elements
   396     if aList[0].endswith(':'):
   400    aList = filter(lambda x: len(x)>0, directory.split('/'))
   397         if theLen > 1:
   401    theLen = len(aList)                     
   398             aList[1] = aList[0] + '/' + aList[1]
   402    # if the first element is a Windows-style disk drive
   399             del aList[0]
   403    # concatenate it with the first directory
   400             theLen -= 1
   404    if aList[0].endswith(':'):
   401     # if the original directory starts at root,
   405       if theLen > 1:
   402     # make sure the first element of the list
   406          aList[1] = aList[0] + '/' + aList[1]
   403     # starts at root too
   407          del aList[0]      
   404     if directory[0] == '/':
   408          theLen -= 1         
   405         aList[0] = '/' + aList[0]
   409    # if the original directory starts at root,
   406     # Now iterate through the list, check if the
   410    # make sure the first element of the list 
   407     # directory exists and if not create it
   411    # starts at root too
   408     theDir = ''
   412    if directory[0] == '/':     
   409     for i in range(theLen):
   413       aList[0] = '/' + aList[0]
   410         theDir += aList[i]
   414    # Now iterate through the list, check if the 
   411         if not os.path.exists(theDir):
   415    # directory exists and if not create it
   412             os.mkdir(theDir)
   416    theDir = ''
   413         theDir += '/'
   417    for i in range(theLen):
   414 
   418       theDir += aList[i]
   415 
   419       if not os.path.exists(theDir):
   416 def unixpath(thePath):
   420          os.mkdir(theDir)
   417     r"""Return a path name that contains Unix separator.
   421       theDir += '/'   
   418 
   422       
   419     [Example]
   423 # -----------------------------------------------------------------------------
   420     >>> unixpath(r"d:\test")
   424 # u n i x p a t h ( )         -- Return a path name that contains Unix separator. --
   421     'd:/test'
   425 # ^^^^^^^^^^^^^^^^^^^
   422     >>> unixpath("d:/test/file.txt")
   426 # 
   423     'd:/test/file.txt'
   427 def unixpath(thePath) :
   424     >>>
   428    r"""Return a path name that contains Unix separator.
   425     """
   429 
   426     thePath = os.path.normpath(thePath)
   430    [Example]
   427     if os.sep == '/':
   431    >>> unixpath(r"d:\test")
   428         return thePath
   432    'd:/test'
   429     else:
   433    >>> unixpath("d:/test/file.txt")
   430         return thePath.replace(os.sep, '/')
   434    'd:/test/file.txt'
   431 
   435    >>> 
   432 
   436    """
   433 # -----------------------------------------------------------------------------
   437    thePath = os.path.normpath(thePath)
       
   438    if os.sep == '/':
       
   439       return thePath
       
   440    else:
       
   441       return thePath.replace(os.sep,'/')
       
   442 
       
   443 # ----------------------------------------------------------------------------- 
       
   444 
   434 
   445 # S c r i p t   e x e c u t i o n               -- Runs when invoked from the command line --
   435 # S c r i p t   e x e c u t i o n               -- Runs when invoked from the command line --
   446 # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   436 # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   447 # 
   437 #
   448 if __name__ == "__main__":
   438 if __name__ == "__main__":
   449     import getopt     # command line parsing
   439     import getopt     # command line parsing
   450     argc = len(sys.argv)
   440     argc = len(sys.argv)
   451     if argc == 1:
   441     if argc == 1:
   452         printUsage('Missing argument: specify at least one of -m or -p (or both).')
   442         printUsage('Missing argument: specify at least one of -m or -p (or both).')
   453         sys.exit(1)
   443         sys.exit(1)
   454     # If there is some arguments, parse the command line
   444     # If there is some arguments, parse the command line
   455     validOptions     = "ehmpv"
   445     validOptions = "ehmpv"
   456     validLongOptions = ['domain=', 'moTarget=']             
   446     validLongOptions = ['domain=', 'moTarget=']
   457     option = {}
   447     option = {}
   458     option['forceEnglish'] = 0
   448     option['forceEnglish'] = 0
   459     option['mo'] = 0
   449     option['mo'] = 0
   460     option['po'] = 0        
   450     option['po'] = 0
   461     option['verbose'] = 0
   451     option['verbose'] = 0
   462     option['domain'] = None
   452     option['domain'] = None
   463     option['moTarget'] = None
   453     option['moTarget'] = None
       
   454     optionKey = {
       
   455         '-e':         'forceEnglish',
       
   456         '-m':         'mo',
       
   457         '-p':         'po',
       
   458         '-v':         'verbose',
       
   459         '--domain':   'domain',
       
   460         '--moTarget': 'moTarget',
       
   461     }
       
   462 
   464     try:
   463     try:
   465         optionList,pargs = getopt.getopt(sys.argv[1:],validOptions,validLongOptions)
   464         optionList, pargs = getopt.getopt(sys.argv[1:], validOptions, validLongOptions)
   466     except getopt.GetoptError, e:
   465     except getopt.GetoptError, e:
   467         printUsage(e[0])
   466         printUsage(e[0])
   468         sys.exit(1)       
   467         sys.exit(1)
   469     for (opt,val) in optionList:
   468     for (opt, val) in optionList:
   470         if  (opt == '-h'):    
   469         if (opt == '-h'):
   471             printUsage()
   470             printUsage()
   472             sys.exit(0) 
   471             sys.exit(0)
   473         elif (opt == '-e'):         option['forceEnglish'] = 1
   472         option[optionKey[opt]] = 1 if val == '' else val
   474         elif (opt == '-m'):         option['mo'] = 1
       
   475         elif (opt == '-p'):         option['po'] = 1
       
   476         elif (opt == '-v'):         option['verbose'] = 1
       
   477         elif (opt == '--domain'):   option['domain'] = val
       
   478         elif (opt == '--moTarget'): option['moTarget'] = val
       
   479     if len(pargs) == 0:
   473     if len(pargs) == 0:
   480         appDirPath = os.getcwd()
   474         appDirPath = os.getcwd()
   481         if option['verbose']:
   475         if option['verbose']:
   482             print "No project directory given. Using current one:  %s" % appDirPath
   476             print "No project directory given. Using current one:  %s" % appDirPath
   483     elif len(pargs) == 1:
   477     elif len(pargs) == 1:
   490         option['domain'] = fileBaseOf(appDirPath)
   484         option['domain'] = fileBaseOf(appDirPath)
   491     if option['verbose']:
   485     if option['verbose']:
   492         print "Application domain used is: '%s'" % option['domain']
   486         print "Application domain used is: '%s'" % option['domain']
   493     if option['po']:
   487     if option['po']:
   494         try:
   488         try:
   495             makePO(appDirPath,option['domain'],option['verbose'])
   489             makePO(appDirPath, option['domain'], option['verbose'])
   496         except IOError, e:
   490         except IOError, e:
   497             printUsage(e[1] + '\n   You must write a file app.fil that contains the list of all files to parse.')
   491             printUsage(e[1] + '\n   You must write a file app.fil that contains the list of all files to parse.')
   498     if option['mo']:
   492     if option['mo']:
   499         makeMO(appDirPath,option['moTarget'],option['domain'],option['verbose'],option['forceEnglish'])
   493         makeMO(appDirPath, option['moTarget'], option['domain'], option['verbose'], option['forceEnglish'])
   500     sys.exit(1)            
   494     sys.exit(1)
   501             
   495 
   502 
   496 
   503 # -----------------------------------------------------------------------------
   497 # -----------------------------------------------------------------------------