http://bugs.debian.org/605615
This happens when the system shell is bash rather than dash. The behaviour of printf when the format string contains the character sequence \' is unspecified. Adding an extra layer of quotation and making sure that everything that uses it goes through printf (so that that extra layer is consistently removed) is easy. I added a gettext_printf function to help with this, which is briefer and involves fewer nested layers of open-coded quotation. The tricky bit is making sure that xgettext understands what's going on! Adding --keyword=gettext_printf is obvious, but I noticed along the way that we weren't setting c-format flags so that translations were checked for the right number of %s entries and the like. I tried adding --flag=gettext_quoted:1:c-format --flag=gettext_printf:1:c-format, but it turns out that xgettext refuses to include c-format flags for strings it extracted from a shell file. Whoops. Since we already had a modified version of po/Makefile.in.in, I changed it a bit further to fix things up with sed. Finally, xgettext refuses to extract strings from here-documents (which I think is probably a bug, and if I can manage to write a reduced test case for it I'll report it to the gettext maintainers). Thus, I extracted translatable strings from here-documents and used a temporary variable for them instead. 2010-12-21 Colin Watson <cjwat...@ubuntu.com> * util/grub-mkconfig_lib.in (gettext_quoted): Add clarifying comment. Add an extra layer of quotation, requiring the output of this function to be used in a printf format string. (gettext_printf): New function. * util/grub.d/10_hurd.in: Use gettext_printf where appropriate. Extract translatable strings from here-documents and use a temporary variable instead, so that xgettext can find them. * util/grub.d/10_kfreebsd.in: Likewise. * util/grub.d/10_linux.in: Likewise. * util/grub.d/20_linux_xen.in: Likewise. * po/grub.d.sed: New file. * po/Makefile.in.in ($(DOMAIN).pot-update): Extract gettext_printf arguments. Set c-format flags on all strings extracted from util/grub.d/ (xgettext refuses to include these itself for strings it extracted from a shell file, but these really are c-format). === modified file 'po/Makefile.in.in' --- po/Makefile.in.in 2010-09-20 23:09:23 +0000 +++ po/Makefile.in.in 2010-12-21 12:05:05 +0000 @@ -173,7 +173,8 @@ $(DOMAIN).pot-update: $(POTFILES) $(srcd --files-from=$(srcdir)/POTFILES-shell.in \ --copyright-holder='$(COPYRIGHT_HOLDER)' \ --msgid-bugs-address="$$msgid_bugs_address" \ - --join-existing --language=Shell --keyword=gettext_quoted \ + --join-existing --language=Shell \ + --keyword=gettext_quoted --keyword=gettext_printf \ ;; \ *) \ $(XGETTEXT) --default-domain=$(DOMAIN) --directory=$(top_srcdir) \ @@ -183,10 +184,13 @@ $(DOMAIN).pot-update: $(POTFILES) $(srcd --package-name="$${package_g...@package@" \ --package-version='@VERSION@' \ --msgid-bugs-address="$$msgid_bugs_address" \ - --join-existing --language=Shell --keyword=gettext_quoted \ + --join-existing --language=Shell \ + --keyword=gettext_quoted --keyword=gettext_printf \ ;; \ esac test ! -f $(DOMAIN).po || { \ + sed -f grub.d.sed < $(DOMAIN).po > $(DOMAIN).1po && \ + mv $(DOMAIN).1po $(DOMAIN).po; \ if test -f $(srcdir)/$(DOMAIN).pot; then \ sed -f remove-potcdate.sed < $(srcdir)/$(DOMAIN).pot > $(DOMAIN).1po && \ sed -f remove-potcdate.sed < $(DOMAIN).po > $(DOMAIN).2po && \ === added file 'po/grub.d.sed' --- po/grub.d.sed 1970-01-01 00:00:00 +0000 +++ po/grub.d.sed 2010-12-21 12:02:27 +0000 @@ -0,0 +1,2 @@ +/^#: util\/grub\.d\//a\ +#, c-format === modified file 'util/grub-mkconfig_lib.in' --- util/grub-mkconfig_lib.in 2010-09-15 12:46:53 +0000 +++ util/grub-mkconfig_lib.in 2010-12-21 12:13:33 +0000 @@ -187,8 +187,20 @@ version_find_latest () echo "$a" } +# One layer of quotation is eaten by "", the second by sed, and the third by +# printf; so this turns ' into \'. Note that you must use the output of +# this function in a printf format string. gettext_quoted () { - $gettext "$@" | sed "s/'/'\\\\''/g" + $gettext "$@" | sed "s/'/'\\\\\\\\''/g" +} + +# Run the first argument through gettext_quoted, and then pass that and all +# remaining arguments to printf. This is a useful abbreviation and tends to +# be easier to type. +gettext_printf () { + local format="$1" + shift + printf "$(gettext_quoted "$format")" "$@" } uses_abstraction () { === modified file 'util/grub.d/10_hurd.in' --- util/grub.d/10_hurd.in 2010-11-01 11:49:40 +0000 +++ util/grub.d/10_hurd.in 2010-12-21 11:45:56 +0000 @@ -81,14 +81,16 @@ do menuentry "${OS} ${KERNEL}" ${CLASS} { EOF prepare_grub_to_access_device ${GRUB_DEVICE_BOOT} | sed -e "s/^/\t/" + message="$(gettext_printf "Loading GNU Mach ...")" cat << EOF - echo '$(gettext_quoted "Loading GNU Mach ...")' + echo '$message' multiboot ${kernel} root=device:${GRUB_DEVICE#/dev/} EOF save_default_entry | sed -e "s/^/\t/" prepare_grub_to_access_device ${GRUB_DEVICE} | sed -e "s/^/\t/" + message="$(gettext_printf "Loading the Hurd ...")" cat << EOF - echo '$(gettext_quoted "Loading the Hurd ...")' + echo '$message' module /hurd/${hurd_fs}.static ${hurd_fs} --readonly \\ --multiboot-command-line='\${kernel-command-line}' \\ --host-priv-port='\${host-port}' \\ @@ -103,13 +105,15 @@ EOF menuentry "${OS} ${KERNEL} (recovery mode)" ${CLASS} { EOF prepare_grub_to_access_device ${GRUB_DEVICE_BOOT} | sed -e "s/^/\t/" + message="$(gettext_printf "Loading GNU Mach ...")" cat << EOF - echo '$(gettext_quoted "Loading GNU Mach ...")' + echo '$message' multiboot ${kernel} root=device:${GRUB_DEVICE#/dev/} -s EOF prepare_grub_to_access_device ${GRUB_DEVICE} | sed -e "s/^/\t/" + message="$(gettext_printf "Loading the Hurd ...")" cat << EOF - echo '$(gettext_quoted "Loading the Hurd ...")' + echo '$message' module /hurd/${hurd_fs}.static ${hurd_fs} \\ --multiboot-command-line='\${kernel-command-line}' \\ --host-priv-port='\${host-port}' \\ === modified file 'util/grub.d/10_kfreebsd.in' --- util/grub.d/10_kfreebsd.in 2010-11-01 11:49:40 +0000 +++ util/grub.d/10_kfreebsd.in 2010-12-21 11:46:13 +0000 @@ -84,8 +84,9 @@ kfreebsd_entry () fi printf '%s\n' "${prepare_boot_cache}" + message="$(gettext_printf "Loading kernel of FreeBSD %s ..." ${version})" cat << EOF - echo '$(printf "$(gettext_quoted "Loading kernel of FreeBSD %s ...")" ${version})' + echo '$message' kfreebsd ${rel_dirname}/${basename} ${args} EOF === modified file 'util/grub.d/10_linux.in' --- util/grub.d/10_linux.in 2010-11-01 11:49:40 +0000 +++ util/grub.d/10_linux.in 2010-12-21 11:43:50 +0000 @@ -94,13 +94,15 @@ EOF prepare_boot_cache="$(prepare_grub_to_access_device ${GRUB_DEVICE_BOOT} | sed -e "s/^/\t/")" fi printf '%s\n' "${prepare_boot_cache}" + message="$(gettext_printf "Loading Linux %s ..." ${version})" cat << EOF - echo '$(printf "$(gettext_quoted "Loading Linux %s ...")" ${version})' + echo '$message' linux ${rel_dirname}/${basename} root=${linux_root_device_thisversion} ro ${args} EOF if test -n "${initrd}" ; then + message="$(gettext_printf "Loading initial ramdisk ...")" cat << EOF - echo '$(gettext_quoted "Loading initial ramdisk ...")' + echo '$message' initrd ${rel_dirname}/${initrd} EOF fi === modified file 'util/grub.d/20_linux_xen.in' --- util/grub.d/20_linux_xen.in 2010-11-01 11:49:40 +0000 +++ util/grub.d/20_linux_xen.in 2010-12-21 11:46:36 +0000 @@ -73,14 +73,16 @@ linux_entry () prepare_boot_cache="$(prepare_grub_to_access_device ${GRUB_DEVICE_BOOT} | sed -e "s/^/\t/")" fi printf '%s\n' "${prepare_boot_cache}" + message="$(gettext_printf "Loading Linux %s ..." ${version})" cat << EOF - echo '$(printf "$(gettext_quoted "Loading Linux %s ...")" ${version})' + echo '$message' multiboot ${rel_xen_dirname}/${xen_basename} placeholder ${xen_args} module ${rel_dirname}/${basename} placeholder root=${linux_root_device_thisversion} ro ${args} EOF if test -n "${initrd}" ; then + message="$(gettext_printf "Loading initial ramdisk ...")" cat << EOF - echo '$(gettext_quoted "Loading initial ramdisk ...")' + echo '$message' module ${rel_dirname}/${initrd} EOF fi -- Colin Watson [cjwat...@ubuntu.com] -- To UNSUBSCRIBE, email to debian-bugs-dist-requ...@lists.debian.org with a subject of "unsubscribe". Trouble? Contact listmas...@lists.debian.org