[idearduino] / trunk / plugin / mki18n.py  
ViewVC logotype

Annotation of /trunk/plugin/mki18n.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3 - (view) (download) (as text)

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

root@fsl.cenditel.gob.ve
ViewVC Help
Powered by ViewVC 1.0.0