--- 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)
+
+
+# -----------------------------------------------------------------------------