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