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