gattrib: use accessors for object visibility
[geda-gaf.git] / build-tools / desktop-i18n
blob8d13aefff24b2bb3abda5af23234b086f5181d8f
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";;
178 --default-domain) domain="$value"; set x "$@" "$arg"; shift;;
180 # We just want to pass this arg straight to xgettext, so
181 # stick it back on the end of the positional parameters
182 set x "$@" "$arg"; shift;;
183 esac
186 # Discard processed arg from positional parameters
187 shift
188 done
190 # If our private data directory exists, die. Otherwise, create it.
191 priv_dir=.po-input
192 if test -d $priv_dir; then
193 echo "desktop-i18n: $PWD/$priv_dir already exists"
194 exit 3
196 mkdir $priv_dir
198 # Process file lists if necessary
199 if test "X$file_lists" != X; then
200 # Extract names of files we need to preprocess
201 desktop_in=`cat $file_lists | grep ".desktop.in$"`
202 xml_in=`cat $file_lists | grep ".xml.in$"`
204 # Create a new POTFILES file which uses the postprocessed
205 # filenames instead of the original ones.
206 cat $file_lists | \
207 sed -e "s:.desktop.in$:.desktop.in.h:" -e "s:.xml.in$:.xml.in.h:" \
208 > $priv_dir/POTFILES
211 # Preprocess .desktop files
212 for f in $desktop_in; do
213 src=`_search_file $f $search_dirs` || { rm -rf $priv_dir; exit 3; }
214 mkdir -p $priv_dir/`dirname $f`
215 extract_desktop $f < $src > $priv_dir/$f.h
216 done
218 # Preprocess .xml files
219 for f in $xml_in; do
220 src=`_search_file $f $search_dirs` || { rm -rf $priv_dir; exit 3; }
221 mkdir -p $priv_dir/`dirname $f`
222 extract_xml $f < $src > $priv_dir/$f.h
223 done
225 # Call xgettext (recall we saved some args in $@)
226 gen_args="--files-from=$priv_dir/POTFILES --directory=$priv_dir"
227 for d in $search_dirs; do
228 gen_args="$gen_args --directory=$d"
229 done
230 $XGETTEXT $gen_args "$@"
232 # Fix up file suffixes. Recall that we added .h to the ends of some
233 # filenames -- now we need to remove them from the generated .pot
234 # files.
235 echo "Fixing up $domain.po"
236 sed -i -e "s/.desktop.in.h/.desktop.in/" -e "s/.xml.in.h/.xml.in/" \
237 $domain.po
239 # Clean up private directory
240 rm -rf $priv_dir
243 _search_file() {
244 f=$1
245 shift
246 for d in $@; do
247 if test -f "$d/$f"; then echo "$d/$f"; exit; fi
248 done
249 echo "desktop-i18n: Cannot find $f in xgettext search directories"
252 # create_desktop INFILE
253 # ---------------------
254 # Parse desktop file data from standard input and generate a
255 # translated desktop file on standard output. If an error occurs, a
256 # message is printed blaming INFILE.
257 create_desktop() {
258 # Loop over each line of standard input
260 while read REPLY; do
261 n=`expr $n + 1`
262 regexp='^_\([^=]*\)=\(.*\)$'
263 if ! (echo $REPLY | grep $regexp > /dev/null); then
264 echo $REPLY
265 continue
267 name=`echo $REPLY | sed -e "s:$regexp:\1:"`
268 msgid=`echo $REPLY | sed -e "s:$regexp:\2:"`
270 # Test for bad characters
271 if (echo $msgid | grep '["\\]' > /dev/null); then
272 echo "$1:$n: msgid contains invalid character" >&2
273 exit 2
276 # Generate first output line
277 echo "$name=$msgid"
279 # Generate language-specific output lines
280 for lang in $LINGUAS; do
281 msg=`LANGUAGE=$lang $GETTEXT "$msgid"`
282 # If translated message is unmodified, don't write an output
283 # line
284 if test "x$msg" = x -o "$msg" = "$msgid"; then
285 continue;
288 echo "$name[$lang]=$msg"
289 done
290 done
293 # create_xml INFILE
294 # -----------------
295 # Parse XML mimeinfo data from standard input and generate a
296 # translated mimeinfo file on standard output. If an error occurs, a
297 # message is printed blaming INFILE.
298 create_xml() {
299 # Loop over each line of standard input
301 while : ; do
303 # We have to do an ugly hack to avoid stripping whitespace.
304 saveIFS="$IFS"
305 IFS=
306 read REPLY || { IFS="$saveIFS" ; break; }
307 IFS="$saveIFS"
309 n=`expr $n + 1`
310 regexp='<_\([a-zA-Z][a-zA-Z]*\)>\(.*\)</_\1>'
311 if ! (echo $REPLY | grep $regexp > /dev/null); then
312 echo "$REPLY"
313 continue
315 name=`echo $REPLY | sed -e "s:$regexp:\1:"`
316 msgid=`echo $REPLY | sed -e "s:$regexp:\2:"`
317 prefix=`echo $REPLY | sed -e "s:^\(.*\)<_$name>.*:\1:"`
318 suffix=`echo $REPLY | sed -e "s:.*</_$name>\(.*\):\1:"`
320 # Test for bad characters
321 if (echo $msgid | grep '["\\]' > /dev/null); then
322 echo "$1:$n: msgid contains invalid character" >&2
323 exit 2
326 # Test for non-empty prefix/suffix
327 if test "x$prefix" != x -o "x$suffix" != x; then
328 echo "$1:$n: translatable tag must be alone on line" >&2
329 exit 2
332 # Generate first output line
333 echo "$REPLY" | sed -e "s:<_\($name\)>\(.*\)</_\1>:<\1>\2</\1>:"
335 # Generate language-specific output lines
336 for lang in $LINGUAS; do
337 msg=`LANGUAGE=$lang $GETTEXT "$msgid"`
338 # If translated message is unmodified, don't write an output
339 # line
340 if test "x$msg" = x -o "$msg" = "$msgid"; then
341 continue;
343 echo "$REPLY" | sed -e "s,<_\($name\)>\(.*\)</_\1>,<\1 xml:lang=\"$lang\">$msg</\1>,"
344 done
345 done
348 # do_create [OPTION]... INFILE OUTFILE
349 # ------------------------------------
350 # Substitutes translations into .desktop or mimeinfo files.
351 do_create() {
352 GETTEXT=gettext
354 # First process command-line arguments
355 for arg; do
356 # Split into name=value
357 name=`echo $arg | sed -e's:=.*::'`
358 value=`echo $arg | sed -e's:^[^=]*=*::'`
360 case $name in
361 --gettext) GETTEXT=$value;;
362 --domain) TEXTDOMAIN=$value;;
363 --localedir) TEXTDOMAINDIR=$value;;
364 --lang) LINGUAS="$LINGUAS $value";;
366 # Arg might be a filename, so save it at the end of the
367 # positional parameters
368 set x "$@" "$arg"; shift
369 esac
371 # Discard processed arg from positional parameters
372 shift
373 done
375 if test $# != 2; then usage 1; fi # Should only have 2 args left
376 INFILE=$1; OUTFILE=$2
377 if ! test -r $INFILE; then
378 echo "desktop-i18n: Cannot open $INFILE for reading."
379 exit 3
382 export TEXTDOMAIN
383 export GETTEXT
384 export TEXTDOMAINDIR
385 export LINGUAS
387 if (echo "$INFILE" | grep ".desktop.in$" > /dev/null); then
388 create_desktop $INFILE < $INFILE > $OUTFILE
389 exit 0
392 if (echo "$INFILE" | grep ".xml.in$" > /dev/null); then
393 create_xml $INFILE < $INFILE > $OUTFILE
394 exit 0
397 echo "desktop-i18n: $INFILE: Unrecognized extension"
398 exit 1
401 # do_setup [DIR]
402 # --------------
403 # Try to set up a source tree to use desktop-i18n.
405 # This is a nasty bit of hackery. We need to insert some rules into
406 # the Makefile.in.in installed by gettextize/autopoint so that make
407 # knows how to generate input for xgettext.
409 # Unfortunately, there's no nice way to do this, so we do it by
410 # appending some rules onto each Makefile.in.in, using the following
411 # procedure:
413 # 1. Look for configure.ac in DIR, or in cwd if DIR wasn't
414 # specified. If we can't find it, whinge.
415 # 2. If configure.ac doesn't have AX_DESKTOP_I18N, quit successfully.
416 # 3. Find anywhere where AC_CONFIG_FILES is called. For each
417 # Makefile.in found in the list of files to create:
418 # (a) Check for Makefile.in.in. If it doesn't exist, skip with a warning.
419 # (b) If Makefile.in.in contains the string DESKTOP_I18N_RULES,
420 # skip silently.
421 # (c) Append a chunk of rules onto Makefile.in.in
423 # Note that we can't use a po/Rules-* file because substitution is not
424 # carried out on these files.
425 do_setup() {
426 # Was DIR specified?
427 if test "x$1" = x; then srcdir=.; else srcdir=$1; fi
429 # Can we find configure.ac or configure.in?
430 for f in configure.ac configure.in; do
431 if test -r $srcdir/$f; then
432 ac_file=$srcdir/$f
433 break
435 done
436 if test "x$ac_file" = x; then
437 echo "Cannot find configure.ac or configure.in!"
438 exit 4
441 # Check that configure.ac is readable
442 if ! test -r $ac_file; then
443 echo "Cannot open $ac_file for reading."
444 exit 3
447 # Is the AX_DESKTOP_I18N macro present?
448 if ! grep AX_DESKTOP_I18N $ac_file > /dev/null; then
449 exit
452 # Now we use a piece of m4 code to try and discover all of the
453 # configuration files. This is UGLY AND BAD, because it only detects
454 # when AC_CONFIG_FILES is called in the main configure script (if
455 # AC_CONFIG_FILES is called by another macro somewhere, it won't be
456 # detected).
457 cat - $ac_file > conftest <<EOF
458 changequote([,])dnl
459 divert([-1])
460 define([AC_CONFIG_FILES], [divert([0])[\$1]divert([-1])])
462 conf_files=`m4 conftest`
463 rm conftest
465 # Look for any files called Makefile.in.
466 for f in $conf_files; do
467 # Discard any composition rules and prepend srcdir.
468 f=`echo "$f" | sed -e 's,:.*,,'`
469 f="$srcdir/$f"
471 # Skip files not called Makefile.in
472 if test `echo "$f" | sed -e 's:.*/::'` != Makefile.in; then
473 continue
476 # Check that a corresponding Makefile.in.in exists and we can
477 # read/write it
478 if ! test -r $f.in -a -w $f.in; then
479 echo "desktop-i18n: Cannot process $f.in"
480 continue
483 # Check that we haven't already hacked it
484 if grep DESKTOP_I18N_RULES $f.in > /dev/null; then
485 continue
488 # Append our rules
489 echo "desktop-i18n: modifying $f.in"
490 cat >> $f.in <<EOF
492 # DESKTOP_I18N_RULES (Do not edit or remove this line)
493 #####################################################################
494 # Makefile rules needed by the desktop-i18n tool.
495 # Copyright (C) 2009-2010 Peter Brett <peter@peter-b.co.uk>
496 # Copyright (C) 2010 Dan McMahill <dan@mcmahill.net>
498 # This program is free software; you can redistribute it and/or modify
499 # it under the terms of the GNU General Public License as published by
500 # the Free Software Foundation; either version 2 of the License, or
501 # (at your option) any later version.
503 # This program is distributed in the hope that it will be useful,
504 # but WITHOUT ANY WARRANTY; without even the implied warranty of
505 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
506 # GNU General Public License for more details.
508 # You should have received a copy of the GNU General Public License
509 # along with this program; if not, write to the Free Software
510 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
512 top_builddir = @top_builddir@
513 MKDIR_P = @MKDIR_P@
515 DESKTOP_I18N_LOCALE_DIR = @DESKTOP_I18N_LOCALE_DIR@
517 # We need to temporarily install the localisation files somewhere so
518 # that desktop-i18n --create can look up messages in them. We recreate
519 # the timestamp before *and* after running 'make install' so that the
520 # make doesn't go into an infinite loop!
521 all: stamp-i18n
522 stamp-i18n: \$(DESKTOP_I18N_LOCALE_DIR) stamp-po Makefile
523 @echo timestamp > stamp-i18nT && mv stamp-i18nT stamp-i18n
524 \$(MAKE) \
525 prefix=\$(DESKTOP_I18N_LOCALE_DIR) \
526 localedir=\$(DESKTOP_I18N_LOCALE_DIR)/share/locale \
527 DESTDIR= install && \
528 cp \$(srcdir)/LINGUAS \$(DESKTOP_I18N_LOCALE_DIR)/\$(DOMAIN).LINGUAS \
529 || rm stamp-i18n
530 @echo timestamp > stamp-i18nT && mv stamp-i18nT stamp-i18n
531 \$(DESKTOP_I18N_LOCALE_DIR):
532 \$(MKDIR_P) \$(DESKTOP_I18N_LOCALE_DIR)
534 clean: clean-i18n
535 clean-i18n:
536 -rm -rf \$(DESKTOP_I18N_LOCALE_DIR) stamp-i18n
538 # End of desktop-i18n rules
539 #####################################################################
542 done
547 # First argument has to be the mode of operation. Then call the
548 # appropriate function to process the rest of the arguments and do the
549 # work.
550 if test -z $1; then usage 1; fi
551 MODE=$1; shift
552 case $MODE in
553 --extract) do_extract "$@";;
554 --create) do_create "$@";;
555 --setup) do_setup "$@";;
556 --help) usage;;
557 *) usage 1;;
558 esac