intltool.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. #!/usr/bin/env python
  2. # encoding: utf-8
  3. # Thomas Nagy, 2006-2018 (ita)
  4. """
  5. Support for translation tools such as msgfmt and intltool
  6. Usage::
  7. def configure(conf):
  8. conf.load('gnu_dirs intltool')
  9. def build(bld):
  10. # process the .po files into .gmo files, and install them in LOCALEDIR
  11. bld(features='intltool_po', appname='myapp', podir='po', install_path="${LOCALEDIR}")
  12. # process an input file, substituting the translations from the po dir
  13. bld(
  14. features = "intltool_in",
  15. podir = "../po",
  16. style = "desktop",
  17. flags = ["-u"],
  18. source = 'kupfer.desktop.in',
  19. install_path = "${DATADIR}/applications",
  20. )
  21. Usage of the :py:mod:`waflib.Tools.gnu_dirs` is recommended, but not obligatory.
  22. """
  23. from __future__ import with_statement
  24. import os, re
  25. from waflib import Context, Task, Utils, Logs
  26. import waflib.Tools.ccroot
  27. from waflib.TaskGen import feature, before_method, taskgen_method
  28. from waflib.Logs import error
  29. from waflib.Configure import conf
  30. _style_flags = {
  31. 'ba': '-b',
  32. 'desktop': '-d',
  33. 'keys': '-k',
  34. 'quoted': '--quoted-style',
  35. 'quotedxml': '--quotedxml-style',
  36. 'rfc822deb': '-r',
  37. 'schemas': '-s',
  38. 'xml': '-x',
  39. }
  40. @taskgen_method
  41. def ensure_localedir(self):
  42. """
  43. Expands LOCALEDIR from DATAROOTDIR/locale if possible, or falls back to PREFIX/share/locale
  44. """
  45. # use the tool gnu_dirs to provide options to define this
  46. if not self.env.LOCALEDIR:
  47. if self.env.DATAROOTDIR:
  48. self.env.LOCALEDIR = os.path.join(self.env.DATAROOTDIR, 'locale')
  49. else:
  50. self.env.LOCALEDIR = os.path.join(self.env.PREFIX, 'share', 'locale')
  51. @before_method('process_source')
  52. @feature('intltool_in')
  53. def apply_intltool_in_f(self):
  54. """
  55. Creates tasks to translate files by intltool-merge::
  56. def build(bld):
  57. bld(
  58. features = "intltool_in",
  59. podir = "../po",
  60. style = "desktop",
  61. flags = ["-u"],
  62. source = 'kupfer.desktop.in',
  63. install_path = "${DATADIR}/applications",
  64. )
  65. :param podir: location of the .po files
  66. :type podir: string
  67. :param source: source files to process
  68. :type source: list of string
  69. :param style: the intltool-merge mode of operation, can be one of the following values:
  70. ``ba``, ``desktop``, ``keys``, ``quoted``, ``quotedxml``, ``rfc822deb``, ``schemas`` and ``xml``.
  71. See the ``intltool-merge`` man page for more information about supported modes of operation.
  72. :type style: string
  73. :param flags: compilation flags ("-quc" by default)
  74. :type flags: list of string
  75. :param install_path: installation path
  76. :type install_path: string
  77. """
  78. try:
  79. self.meths.remove('process_source')
  80. except ValueError:
  81. pass
  82. self.ensure_localedir()
  83. podir = getattr(self, 'podir', '.')
  84. podirnode = self.path.find_dir(podir)
  85. if not podirnode:
  86. error("could not find the podir %r" % podir)
  87. return
  88. cache = getattr(self, 'intlcache', '.intlcache')
  89. self.env.INTLCACHE = [os.path.join(str(self.path.get_bld()), podir, cache)]
  90. self.env.INTLPODIR = podirnode.bldpath()
  91. self.env.append_value('INTLFLAGS', getattr(self, 'flags', self.env.INTLFLAGS_DEFAULT))
  92. if '-c' in self.env.INTLFLAGS:
  93. self.bld.fatal('Redundant -c flag in intltool task %r' % self)
  94. style = getattr(self, 'style', None)
  95. if style:
  96. try:
  97. style_flag = _style_flags[style]
  98. except KeyError:
  99. self.bld.fatal('intltool_in style "%s" is not valid' % style)
  100. self.env.append_unique('INTLFLAGS', [style_flag])
  101. for i in self.to_list(self.source):
  102. node = self.path.find_resource(i)
  103. task = self.create_task('intltool', node, node.change_ext(''))
  104. inst = getattr(self, 'install_path', None)
  105. if inst:
  106. self.add_install_files(install_to=inst, install_from=task.outputs)
  107. @feature('intltool_po')
  108. def apply_intltool_po(self):
  109. """
  110. Creates tasks to process po files::
  111. def build(bld):
  112. bld(features='intltool_po', appname='myapp', podir='po', install_path="${LOCALEDIR}")
  113. The relevant task generator arguments are:
  114. :param podir: directory of the .po files
  115. :type podir: string
  116. :param appname: name of the application
  117. :type appname: string
  118. :param install_path: installation directory
  119. :type install_path: string
  120. The file LINGUAS must be present in the directory pointed by *podir* and list the translation files to process.
  121. """
  122. try:
  123. self.meths.remove('process_source')
  124. except ValueError:
  125. pass
  126. self.ensure_localedir()
  127. appname = getattr(self, 'appname', getattr(Context.g_module, Context.APPNAME, 'set_your_app_name'))
  128. podir = getattr(self, 'podir', '.')
  129. inst = getattr(self, 'install_path', '${LOCALEDIR}')
  130. linguas = self.path.find_node(os.path.join(podir, 'LINGUAS'))
  131. if linguas:
  132. # scan LINGUAS file for locales to process
  133. with open(linguas.abspath()) as f:
  134. langs = []
  135. for line in f.readlines():
  136. # ignore lines containing comments
  137. if not line.startswith('#'):
  138. langs += line.split()
  139. re_linguas = re.compile('[-a-zA-Z_@.]+')
  140. for lang in langs:
  141. # Make sure that we only process lines which contain locales
  142. if re_linguas.match(lang):
  143. node = self.path.find_resource(os.path.join(podir, re_linguas.match(lang).group() + '.po'))
  144. task = self.create_task('po', node, node.change_ext('.mo'))
  145. if inst:
  146. filename = task.outputs[0].name
  147. (langname, ext) = os.path.splitext(filename)
  148. inst_file = inst + os.sep + langname + os.sep + 'LC_MESSAGES' + os.sep + appname + '.mo'
  149. self.add_install_as(install_to=inst_file, install_from=task.outputs[0],
  150. chmod=getattr(self, 'chmod', Utils.O644))
  151. else:
  152. Logs.pprint('RED', "Error no LINGUAS file found in po directory")
  153. class po(Task.Task):
  154. """
  155. Compiles .po files into .gmo files
  156. """
  157. run_str = '${MSGFMT} -o ${TGT} ${SRC}'
  158. color = 'BLUE'
  159. class intltool(Task.Task):
  160. """
  161. Calls intltool-merge to update translation files
  162. """
  163. run_str = '${INTLTOOL} ${INTLFLAGS} ${INTLCACHE_ST:INTLCACHE} ${INTLPODIR} ${SRC} ${TGT}'
  164. color = 'BLUE'
  165. @conf
  166. def find_msgfmt(conf):
  167. """
  168. Detects msgfmt and sets the ``MSGFMT`` variable
  169. """
  170. conf.find_program('msgfmt', var='MSGFMT')
  171. @conf
  172. def find_intltool_merge(conf):
  173. """
  174. Detects intltool-merge
  175. """
  176. if not conf.env.PERL:
  177. conf.find_program('perl', var='PERL')
  178. conf.env.INTLCACHE_ST = '--cache=%s'
  179. conf.env.INTLFLAGS_DEFAULT = ['-q', '-u']
  180. conf.find_program('intltool-merge', interpreter='PERL', var='INTLTOOL')
  181. def configure(conf):
  182. """
  183. Detects the program *msgfmt* and set *conf.env.MSGFMT*.
  184. Detects the program *intltool-merge* and set *conf.env.INTLTOOL*.
  185. It is possible to set INTLTOOL in the environment, but it must not have spaces in it::
  186. $ INTLTOOL="/path/to/the program/intltool" waf configure
  187. If a C/C++ compiler is present, execute a compilation test to find the header *locale.h*.
  188. """
  189. conf.find_msgfmt()
  190. conf.find_intltool_merge()
  191. if conf.env.CC or conf.env.CXX:
  192. conf.check(header_name='locale.h')