Manually ran make update-po in order to prep for the make distcheck
[geda-gaf/peter-b.git] / build-tools / desktop-i18n
blob9ec0c060601400a90e82046d639ac3dc60447914
1 #!/bin/sh
2 # -*-Shell-script-*-
3 # Helper script for translating desktop integration data
4 # Copyright (C) 2009-2010 Peter Brett <peter@peter-b.co.uk>
5 # Copyright (C) 2010 Dan McMahill <dan@mcmahill.net>
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 # usage [EXITSTATUS]
22 # ------------------
23 # Print a simple help message, then exit with EXITSTATUS
24 usage() {
25 cat <<EOF
26 Carry out translation tasks on desktop integration data.
28 Usage:
29 desktop-i18n --extract <options> -- <xgettext_options>
30 desktop-i18n --create <options> INFILE OUTFILE
31 desktop-i18n --setup
33 Operating modes:
34 --extract Extract strings by calling xgettext
35 --create Substitute translated strings
36 --setup Setup a source tree to use desktop-i18n
37 --help Print this message
39 Options for --extract mode:
40 --xgettext=XGETTEXT
41 Specify xgettext executable to use
43 Options for --create mode:
44 --gettext=GETTEXT
45 Specify gettext executable to use
46 --domain=TEXTDOMAIN
47 Retrieve translated bmessages from TEXTDOMAIN
48 --localedir=TEXTDOMAINDIR
49 Retrieve message catalog from TEXTDOMAINDIR
50 --lang=LANG Add a language to translate messages into
52 In order for this to work, all strings to be matched must be on a
53 single line. In a .desktop file, a translatable name-value pair must
54 have the desired name prefixed by an underscore. For example:
56 _Comment=gEDA Schematic Editor
58 In a MIME info file, XML tag pairs where the tagname begins with an
59 underscore are recognized. Both tags must be on the same line, and
60 the tag must be the only XML content on the line. Whitespace at the
61 start of the line before the opening tag is preserved. For example:
63 <_comment>gEDA circuit schematic</_comment>
65 Do not include double-quotes (") or slashes (\) in translatable
66 strings.
67 EOF
68 exit $1
71 # extract_desktop INFILE
72 # ----------------------
73 # Parse desktop file data from standard input and generate C on
74 # standard output. If an error occurs, a message is printed blaming
75 # INFILE.
76 extract_desktop() {
77 # First argument is name of file being processed
78 echo "/* Generated from $1 by desktop-i18n */"
79 echo
80 # Loop over each line of standard input
81 n=0
82 while read REPLY; do
83 n=`expr $n + 1`
84 regexp='^_\([^=]*\)=\(.*\)$'
85 if ! (echo $REPLY | grep $regexp > /dev/null); then
86 continue
88 name=`echo $REPLY | sed -e "s:$regexp:\1:"`
89 msgid=`echo $REPLY | sed -e "s:$regexp:\2:"`
91 # Test for bad characters
92 if (echo $msgid | grep '["\\]' > /dev/null); then
93 echo "$1:$n: msgid contains invalid character" >&2
94 exit 2
97 # Generate output line
98 if test "x$name" != x -a "x$msgid" != x; then
99 echo "_(\"$msgid\");"
100 else
101 echo "$1:$n: name or msgid is empty" >&2
102 exit 2
104 done
108 # extract_xml INFILE
109 # ------------------
110 # Parse XML mimeinfo data from standard input and generate C on
111 # standard output. If an error occurs, a message is printed blaming
112 # INFILE.
113 extract_xml() {
114 echo "/* Generated from $1 by desktop-i18n */"
115 echo
116 # Loop over each line of standard input
118 while read REPLY; do
119 n=`expr $n + 1`
120 regexp='<_\([a-zA-Z][a-zA-Z]*\)>\(.*\)</_\1>'
121 if ! (echo $REPLY | grep $regexp > /dev/null); then
122 continue
124 name=`echo $REPLY | sed -e "s:$regexp:\1:"`
125 msgid=`echo $REPLY | sed -e "s:$regexp:\2:"`
127 # Test for bad characters
128 if (echo $msgid | grep '["\\]' > /dev/null); then
129 echo "$1:$n: msgid contains invalid character" >&2
130 exit 2
133 # Generate output line
134 if test "x$name" != x -a "x$msgid" != x; then
135 echo "_(\"$msgid\");"
136 else
137 echo "$1:$n: name or msgid is empty" >&2
138 exit 2
140 done
143 # do_extract [OPTION]... -- [XGETTEXT_OPTION]...
144 # ----------------------------------------------
145 # A wrapper around xgettext. It identifies the file lists and search
146 # directories being used by xgettext, and from them any desktop or
147 # mimeinfo files to be processed.
149 # It then creates a new private directory, and adds it to the xgettext
150 # search path. It creates a new file list and set of preprocessed
151 # files in that directory, and then calls xgettext (preserving all
152 # other original arguments).
153 do_extract() {
154 XGETTEXT=xgettext
156 # First we have to process the command-line arguments
157 for arg; do
158 # Split into name=value
159 name=`echo $arg | sed -e's:=.*::'`
160 value=`echo $arg | sed -e's:^[^=]*=*::'`
162 if test "X$in_xg_args" = X; then
163 # This is an argument only for this script
164 case $name in
165 --xgettext) XGETTEXT="$value";;
166 --help) usage;;
167 --) in_xg_args=1;;
168 *) usage 1;;
169 esac
171 else
172 # This is an argument to xgettext. Luckily the Makefile only
173 # uses full-length arguments, and we only really care about the
174 # ones from there!
175 case $name in
176 --directory) search_dirs="$search_dirs $value";;
177 --files-from) file_lists="$file_lists $value";;
179 # We just want to pass this arg straight to xgettext, so
180 # stick it back on the end of the positional parameters
181 set x "$@" "$arg"; shift;;
182 esac
185 # Discard processed arg from positional parameters
186 shift
187 done
189 # If our private data directory exists, die. Otherwise, create it.
190 priv_dir=.po-input
191 if test -d $priv_dir; then
192 echo "desktop-i18n: $PWD/$priv_dir already exists"
193 exit 3
195 mkdir $priv_dir
197 # Process file lists if necessary
198 if test "X$file_lists" != X; then
199 # Extract names of files we need to preprocess
200 desktop_in=`cat $file_lists | grep ".desktop.in$"`
201 xml_in=`cat $file_lists | grep ".xml.in$"`
203 # Create a new POTFILES file which uses the postprocessed
204 # filenames instead of the original ones.
205 cat $file_lists | \
206 sed -e "s:.desktop.in$:.desktop.in.h:" -e "s:.xml.in$:.xml.in.h:" \
207 > $priv_dir/POTFILES
210 # Preprocess .desktop files
211 for f in $desktop_in; do
212 src=`_search_file $f $search_dirs` || { rm -rf $priv_dir; exit 3; }
213 mkdir -p $priv_dir/`dirname $f`
214 extract_desktop $f < $src > $priv_dir/$f.h
215 done
217 # Preprocess .xml files
218 for f in $xml_in; do
219 src=`_search_file $f $search_dirs` || { rm -rf $priv_dir; exit 3; }
220 mkdir -p $priv_dir/`dirname $f`
221 extract_xml $f < $src > $priv_dir/$f.h
222 done
224 # Call xgettext (recall we saved some args in $@)
225 gen_args="--files-from=$priv_dir/POTFILES --directory=$priv_dir"
226 for d in $search_dirs; do
227 gen_args="$gen_args --directory=$d"
228 done
229 $XGETTEXT $gen_args "$@"
231 # Clean up private directory
232 rm -rf $priv_dir
235 _search_file() {
236 f=$1
237 shift
238 for d in $@; do
239 if test -f "$d/$f"; then echo "$d/$f"; exit; fi
240 done
241 echo "desktop-i18n: Cannot find $f in xgettext search directories"
244 # create_desktop INFILE
245 # ---------------------
246 # Parse desktop file data from standard input and generate a
247 # translated desktop file on standard output. If an error occurs, a
248 # message is printed blaming INFILE.
249 create_desktop() {
250 # Loop over each line of standard input
252 while read REPLY; do
253 n=`expr $n + 1`
254 regexp='^_\([^=]*\)=\(.*\)$'
255 if ! (echo $REPLY | grep $regexp > /dev/null); then
256 echo $REPLY
257 continue
259 name=`echo $REPLY | sed -e "s:$regexp:\1:"`
260 msgid=`echo $REPLY | sed -e "s:$regexp:\2:"`
262 # Test for bad characters
263 if (echo $msgid | grep '["\\]' > /dev/null); then
264 echo "$1:$n: msgid contains invalid character" >&2
265 exit 2
268 # Generate first output line
269 echo "$name=$msgid"
271 # Generate language-specific output lines
272 for lang in $LINGUAS; do
273 msg=`LANGUAGE=$lang $GETTEXT "$msgid"`
274 # If translated message is unmodified, don't write an output
275 # line
276 if test "x$msg" = x -o "$msg" = "$msgid"; then
277 continue;
280 echo "$name[$lang]=$msg"
281 done
282 done
285 # create_xml INFILE
286 # -----------------
287 # Parse XML mimeinfo data from standard input and generate a
288 # translated mimeinfo file on standard output. If an error occurs, a
289 # message is printed blaming INFILE.
290 create_xml() {
291 # Loop over each line of standard input
293 while : ; do
295 # We have to do an ugly hack to avoid stripping whitespace.
296 saveIFS="$IFS"
297 IFS=
298 read REPLY || { IFS="$saveIFS" ; break; }
299 IFS="$saveIFS"
301 n=`expr $n + 1`
302 regexp='<_\([a-zA-Z][a-zA-Z]*\)>\(.*\)</_\1>'
303 if ! (echo $REPLY | grep $regexp > /dev/null); then
304 echo "$REPLY"
305 continue
307 name=`echo $REPLY | sed -e "s:$regexp:\1:"`
308 msgid=`echo $REPLY | sed -e "s:$regexp:\2:"`
309 prefix=`echo $REPLY | sed -e "s:^\(.*\)<_$name>.*:\1:"`
310 suffix=`echo $REPLY | sed -e "s:.*</_$name>\(.*\):\1:"`
312 # Test for bad characters
313 if (echo $msgid | grep '["\\]' > /dev/null); then
314 echo "$1:$n: msgid contains invalid character" >&2
315 exit 2
318 # Test for non-empty prefix/suffix
319 if test "x$prefix" != x -o "x$suffix" != x; then
320 echo "$1:$n: translatable tag must be alone on line" >&2
321 exit 2
324 # Generate first output line
325 echo "$REPLY" | sed -e "s:<_\($name\)>\(.*\)</_\1>:<\1>\2</\1>:"
327 # Generate language-specific output lines
328 for lang in $LINGUAS; do
329 msg=`LANGUAGE=$lang $GETTEXT "$msgid"`
330 # If translated message is unmodified, don't write an output
331 # line
332 if test "x$msg" = x -o "$msg" = "$msgid"; then
333 continue;
335 echo "$REPLY" | sed -e "s,<_\($name\)>\(.*\)</_\1>,<\1 xml:lang=\"$lang\">$msg</\1>,"
336 done
337 done
340 # do_create [OPTION]... INFILE OUTFILE
341 # ------------------------------------
342 # Substitutes translations into .desktop or mimeinfo files.
343 do_create() {
344 GETTEXT=gettext
346 # First process command-line arguments
347 for arg; do
348 # Split into name=value
349 name=`echo $arg | sed -e's:=.*::'`
350 value=`echo $arg | sed -e's:^[^=]*=*::'`
352 case $name in
353 --gettext) GETTEXT=$value;;
354 --domain) TEXTDOMAIN=$value;;
355 --localedir) TEXTDOMAINDIR=$value;;
356 --lang) LINGUAS="$LINGUAS $value";;
358 # Arg might be a filename, so save it at the end of the
359 # positional parameters
360 set x "$@" "$arg"; shift
361 esac
363 # Discard processed arg from positional parameters
364 shift
365 done
367 if test $# != 2; then usage 1; fi # Should only have 2 args left
368 INFILE=$1; OUTFILE=$2
369 if ! test -r $INFILE; then
370 echo "desktop-i18n: Cannot open $INFILE for reading."
371 exit 3
374 export TEXTDOMAIN
375 export GETTEXT
376 export TEXTDOMAINDIR
377 export LINGUAS
379 if (echo "$INFILE" | grep ".desktop.in$" > /dev/null); then
380 create_desktop $INFILE < $INFILE > $OUTFILE
381 exit 0
384 if (echo "$INFILE" | grep ".xml.in$" > /dev/null); then
385 create_xml $INFILE < $INFILE > $OUTFILE
386 exit 0
389 echo "desktop-i18n: $INFILE: Unrecognized extension"
390 exit 1
393 # do_setup [DIR]
394 # --------------
395 # Try to set up a source tree to use desktop-i18n.
397 # This is a nasty bit of hackery. We need to insert some rules into
398 # the Makefile.in.in installed by gettextize/autopoint so that make
399 # knows how to generate input for xgettext.
401 # Unfortunately, there's no nice way to do this, so we do it by
402 # appending some rules onto each Makefile.in.in, using the following
403 # procedure:
405 # 1. Look for configure.ac in DIR, or in cwd if DIR wasn't
406 # specified. If we can't find it, whinge.
407 # 2. If configure.ac doesn't have AX_DESKTOP_I18N, quit successfully.
408 # 3. Find anywhere where AC_CONFIG_FILES is called. For each
409 # Makefile.in found in the list of files to create:
410 # (a) Check for Makefile.in.in. If it doesn't exist, skip with a warning.
411 # (b) If Makefile.in.in contains the string DESKTOP_I18N_RULES,
412 # skip silently.
413 # (c) Append a chunk of rules onto Makefile.in.in
415 # Note that we can't use a po/Rules-* file because substitution is not
416 # carried out on these files.
417 do_setup() {
418 # Was DIR specified?
419 if test "x$1" = x; then srcdir=.; else srcdir=$1; fi
421 # Can we find configure.ac or configure.in?
422 for f in configure.ac configure.in; do
423 if test -r $srcdir/$f; then
424 ac_file=$srcdir/$f
425 break
427 done
428 if test "x$ac_file" = x; then
429 echo "Cannot find configure.ac or configure.in!"
430 exit 4
433 # Check that configure.ac is readable
434 if ! test -r $ac_file; then
435 echo "Cannot open $ac_file for reading."
436 exit 3
439 # Is the AX_DESKTOP_I18N macro present?
440 if ! grep AX_DESKTOP_I18N $ac_file > /dev/null; then
441 exit
444 # Now we use a piece of m4 code to try and discover all of the
445 # configuration files. This is UGLY AND BAD, because it only detects
446 # when AC_CONFIG_FILES is called in the main configure script (if
447 # AC_CONFIG_FILES is called by another macro somewhere, it won't be
448 # detected).
449 cat - $ac_file > conftest <<EOF
450 changequote([,])dnl
451 divert([-1])
452 define([AC_CONFIG_FILES], [divert([0])[\$1]divert([-1])])
454 conf_files=`m4 conftest`
455 rm conftest
457 # Look for any files called Makefile.in.
458 for f in $conf_files; do
459 # Discard any composition rules and prepend srcdir.
460 f=`echo "$f" | sed -e 's,:.*,,'`
461 f="$srcdir/$f"
463 # Skip files not called Makefile.in
464 if test `echo "$f" | sed -e 's:.*/::'` != Makefile.in; then
465 continue
468 # Check that a corresponding Makefile.in.in exists and we can
469 # read/write it
470 if ! test -r $f.in -a -w $f.in; then
471 echo "desktop-i18n: Cannot process $f.in"
472 continue
475 # Check that we haven't already hacked it
476 if grep DESKTOP_I18N_RULES $f.in > /dev/null; then
477 continue
480 # Append our rules
481 echo "desktop-i18n: modifying $f.in"
482 cat >> $f.in <<EOF
484 # DESKTOP_I18N_RULES (Do not edit or remove this line)
485 #####################################################################
486 # Makefile rules needed by the desktop-i18n tool.
487 # Copyright (C) 2009-2010 Peter Brett <peter@peter-b.co.uk>
488 # Copyright (C) 2010 Dan McMahill <dan@mcmahill.net>
490 # This program is free software; you can redistribute it and/or modify
491 # it under the terms of the GNU General Public License as published by
492 # the Free Software Foundation; either version 2 of the License, or
493 # (at your option) any later version.
495 # This program is distributed in the hope that it will be useful,
496 # but WITHOUT ANY WARRANTY; without even the implied warranty of
497 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
498 # GNU General Public License for more details.
500 # You should have received a copy of the GNU General Public License
501 # along with this program; if not, write to the Free Software
502 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
504 top_builddir = @top_builddir@
505 MKDIR_P = @MKDIR_P@
507 DESKTOP_I18N_LOCALE_DIR = @DESKTOP_I18N_LOCALE_DIR@
509 # We need to temporarily install the localisation files somewhere so
510 # that desktop-i18n --create can look up messages in them. We recreate
511 # the timestamp before *and* after running 'make install' so that the
512 # make doesn't go into an infinite loop!
513 all: stamp-i18n
514 stamp-i18n: \$(DESKTOP_I18N_LOCALE_DIR) stamp-po Makefile
515 @echo timestamp > stamp-i18nT && mv stamp-i18nT stamp-i18n
516 \$(MAKE) \
517 prefix=\$(DESKTOP_I18N_LOCALE_DIR) \
518 localedir=\$(DESKTOP_I18N_LOCALE_DIR)/share/locale \
519 DESTDIR= install && \
520 cp \$(srcdir)/LINGUAS \$(DESKTOP_I18N_LOCALE_DIR)/\$(DOMAIN).LINGUAS \
521 || rm stamp-i18n
522 @echo timestamp > stamp-i18nT && mv stamp-i18nT stamp-i18n
523 \$(DESKTOP_I18N_LOCALE_DIR):
524 \$(MKDIR_P) \$(DESKTOP_I18N_LOCALE_DIR)
526 clean: clean-i18n
527 clean-i18n:
528 -rm -rf \$(DESKTOP_I18N_LOCALE_DIR) stamp-i18n
530 # End of desktop-i18n rules
531 #####################################################################
534 done
539 # First argument has to be the mode of operation. Then call the
540 # appropriate function to process the rest of the arguments and do the
541 # work.
542 if test -z $1; then usage 1; fi
543 MODE=$1; shift
544 case $MODE in
545 --extract) do_extract "$@";;
546 --create) do_create "$@";;
547 --setup) do_setup "$@";;
548 --help) usage;;
549 *) usage 1;;
550 esac