From: Bruno Haible Date: Tue, 18 Feb 2003 13:12:57 +0000 (+0000) Subject: New facility for logging untranslated messages to a file. X-Git-Tag: v0.12~215 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8b12dd3dbae5f294af0357a18b5dce7ea3753d38;p=thirdparty%2Fgettext.git New facility for logging untranslated messages to a file. --- diff --git a/NEWS b/NEWS index fb52234ab..7aedbe929 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,9 @@ Version 0.11.6 - October 2002 New documentation section: - Writing your own programs that process PO files. +* New documentation section: + - Prioritizing messages: How to determine which messages to translate first. + * xgettext now supports msgid strings in other encodings than ASCII. xgettext has a new option --from-code that specifies the encoding of the source files. The resulting POT files are UTF-8 encoded. diff --git a/PACKAGING b/PACKAGING index 44d80ec91..b74fc8e31 100644 --- a/PACKAGING +++ b/PACKAGING @@ -94,6 +94,7 @@ following file list. $prefix/lib/libgettextlib* $prefix/lib/libgettextsrc* $prefix/lib/libgettextpo* + $prefix/lib/preloadable_libintl.so (only installed on glibc systems) $prefix/lib/gettext/* $prefix/share/locale/*/LC_MESSAGES/gettext-tools.mo $prefix/share/gettext/config.rpath diff --git a/gettext-runtime/intl/ChangeLog b/gettext-runtime/intl/ChangeLog index 9b00b1386..d5983750f 100644 --- a/gettext-runtime/intl/ChangeLog +++ b/gettext-runtime/intl/ChangeLog @@ -1,3 +1,15 @@ +2003-02-16 Bruno Haible + + * log.c: New file. + * dcigettext.c (DCIGETTEXT) [!_LIBC]: Before returning the + untranslated string, call _nl_log_untranslated. + * intl-compat.c: Comments. + * Makefile.in (SOURCES): Add log.c. + (OBJECTS): Add intl-compat.$lo unconditionally. Add log.$lo. + (log.lo): New rule. + (install-exec): Stop using @INTLOBJS@. Install preloadable_libintl.so. + (installdirs, uninstall): Update accordingly. + 2003-02-16 Bruno Haible * Makefile.in (.sin.sed): Remove rule. diff --git a/gettext-runtime/intl/Makefile.in b/gettext-runtime/intl/Makefile.in index 1472b7ab7..030a258d7 100644 --- a/gettext-runtime/intl/Makefile.in +++ b/gettext-runtime/intl/Makefile.in @@ -87,10 +87,11 @@ SOURCES = \ plural-exp.c \ localcharset.c \ localename.c \ + log.c \ osdep.c \ os2compat.c \ intl-compat.c -OBJECTS = @INTLOBJS@ \ +OBJECTS = \ bindtextdom.$lo \ dcgettext.$lo \ dgettext.$lo \ @@ -109,8 +110,9 @@ OBJECTS = @INTLOBJS@ \ plural-exp.$lo \ localcharset.$lo \ localename.$lo \ - osdep.$lo -GETTOBJS = intl-compat.$lo + log.$lo \ + osdep.$lo \ + intl-compat.$lo DISTFILES.common = Makefile.in \ config.charset locale.alias ref-add.sin ref-del.sin $(HEADERS) $(SOURCES) DISTFILES.generated = plural.c @@ -193,6 +195,8 @@ localcharset.lo: $(srcdir)/localcharset.c $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/localcharset.c localename.lo: $(srcdir)/localename.c $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/localename.c +log.lo: $(srcdir)/log.c + $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/log.c osdep.lo: $(srcdir)/osdep.c $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/osdep.c intl-compat.lo: $(srcdir)/intl-compat.c @@ -227,7 +231,7 @@ check: all install: install-exec install-data install-exec: all if { test "$(PACKAGE)" = "gettext-runtime" || test "$(PACKAGE)" = "gettext-tools"; } \ - && test '@INTLOBJS@' = '$(GETTOBJS)'; then \ + && test '@USE_INCLUDED_LIBINTL@' = yes; then \ $(mkinstalldirs) $(DESTDIR)$(libdir) $(DESTDIR)$(includedir); \ $(INSTALL_DATA) libintl.h $(DESTDIR)$(includedir)/libintl.h; \ $(LIBTOOL) --mode=install \ @@ -235,6 +239,18 @@ install-exec: all else \ : ; \ fi + if test "$(PACKAGE)" = "gettext-tools" \ + && test '@USE_INCLUDED_LIBINTL@' = no; then \ + $(mkinstalldirs) $(DESTDIR)$(libdir); \ + $(LIBTOOL) --mode=install \ + $(INSTALL_DATA) libgnuintl.$la $(DESTDIR)$(libdir)/libgnuintl.$la; \ + rm -f $(DESTDIR)$(libdir)/preloadable_libintl.so; \ + $(INSTALL_DATA) $(DESTDIR)$(libdir)/libgnuintl.so $(DESTDIR)$(libdir)/preloadable_libintl.so; \ + $(LIBTOOL) --mode=uninstall \ + rm -f $(DESTDIR)$(libdir)/libgnuintl.$la; \ + else \ + : ; \ + fi if test '@USE_INCLUDED_LIBINTL@' = yes; then \ test @GLIBC21@ != no || $(mkinstalldirs) $(DESTDIR)$(libdir); \ temp=$(DESTDIR)$(libdir)/t-charset.alias; \ @@ -293,11 +309,17 @@ install-strip: install installdirs: if { test "$(PACKAGE)" = "gettext-runtime" || test "$(PACKAGE)" = "gettext-tools"; } \ - && test '@INTLOBJS@' = '$(GETTOBJS)'; then \ + && test '@USE_INCLUDED_LIBINTL@' = yes; then \ $(mkinstalldirs) $(DESTDIR)$(libdir) $(DESTDIR)$(includedir); \ else \ : ; \ fi + if test "$(PACKAGE)" = "gettext-tools" \ + && test '@USE_INCLUDED_LIBINTL@' = no; then \ + $(mkinstalldirs) $(DESTDIR)$(libdir); \ + else \ + : ; \ + fi if test '@USE_INCLUDED_LIBINTL@' = yes; then \ test @GLIBC21@ != no || $(mkinstalldirs) $(DESTDIR)$(libdir); \ $(mkinstalldirs) $(DESTDIR)$(localedir); \ @@ -315,13 +337,19 @@ installcheck: uninstall: if { test "$(PACKAGE)" = "gettext-runtime" || test "$(PACKAGE)" = "gettext-tools"; } \ - && test '@INTLOBJS@' = '$(GETTOBJS)'; then \ + && test '@USE_INCLUDED_LIBINTL@' = yes; then \ rm -f $(DESTDIR)$(includedir)/libintl.h; \ $(LIBTOOL) --mode=uninstall \ rm -f $(DESTDIR)$(libdir)/libintl.$la; \ else \ : ; \ fi + if test "$(PACKAGE)" = "gettext-tools" \ + && test '@USE_INCLUDED_LIBINTL@' = no; then \ + rm -f $(DESTDIR)$(libdir)/preloadable_libintl.so; \ + else \ + : ; \ + fi if test '@USE_INCLUDED_LIBINTL@' = yes; then \ if test -f $(DESTDIR)$(libdir)/charset.alias; then \ temp=$(DESTDIR)$(libdir)/t-charset.alias; \ diff --git a/gettext-runtime/intl/dcigettext.c b/gettext-runtime/intl/dcigettext.c index 55aaa060f..593adb09f 100644 --- a/gettext-runtime/intl/dcigettext.c +++ b/gettext-runtime/intl/dcigettext.c @@ -550,17 +550,9 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) } if (ret == NULL) - { - /* We cannot get the current working directory. Don't signal an - error but simply return the default string. */ - FREE_BLOCKS (block_list); - __libc_rwlock_unlock (_nl_state_lock); - __set_errno (saved_errno); - return (plural == 0 - ? (char *) msgid1 - /* Use the Germanic plural rule. */ - : n == 1 ? (char *) msgid1 : (char *) msgid2); - } + /* We cannot get the current working directory. Don't signal an + error but simply return the default string. */ + goto return_untranslated; stpcpy (stpcpy (strchr (dirname, '\0'), "/"), binding->dirname); } @@ -617,16 +609,7 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) domain. Return the MSGID. */ if (strcmp (single_locale, "C") == 0 || strcmp (single_locale, "POSIX") == 0) - { - FREE_BLOCKS (block_list); - __libc_rwlock_unlock (_nl_state_lock); - __set_errno (saved_errno); - return (plural == 0 - ? (char *) msgid1 - /* Use the Germanic plural rule. */ - : n == 1 ? (char *) msgid1 : (char *) msgid2); - } - + break; /* Find structure describing the message catalog matching the DOMAINNAME and CATEGORY. */ @@ -707,7 +690,30 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) } } } - /* NOTREACHED */ + + return_untranslated: + /* Return the untranslated MSGID. */ + FREE_BLOCKS (block_list); + __libc_rwlock_unlock (_nl_state_lock); +#ifndef _LIBC + if (!ENABLE_SECURE) + { + extern void _nl_log_untranslated PARAMS ((const char *logfilename, + const char *domainname, + const char *msgid1, + const char *msgid2, + int plural)); + const char *logfilename = getenv ("GETTEXT_LOG_UNTRANSLATED"); + + if (logfilename != NULL && logfilename[0] != '\0') + _nl_log_untranslated (logfilename, domainname, msgid1, msgid2, plural); + } +#endif + __set_errno (saved_errno); + return (plural == 0 + ? (char *) msgid1 + /* Use the Germanic plural rule. */ + : n == 1 ? (char *) msgid1 : (char *) msgid2); } diff --git a/gettext-runtime/intl/intl-compat.c b/gettext-runtime/intl/intl-compat.c index da890159f..129c60e5b 100644 --- a/gettext-runtime/intl/intl-compat.c +++ b/gettext-runtime/intl/intl-compat.c @@ -1,6 +1,6 @@ /* intl-compat.c - Stub functions to call gettext functions from GNU gettext Library. - Copyright (C) 1995, 2000-2002 Software Foundation, Inc. + Copyright (C) 1995, 2000-2003 Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published @@ -31,7 +31,10 @@ defined in the included GNU libintl library (with "libintl_" prefix). It is compiled into libintl in order to make the AM_GNU_GETTEXT test of gettext <= 0.11.2 work with the libintl library >= 0.11.3 which - has the redirections primarily in the include file. */ + has the redirections primarily in the include file. + It is also compiled into libgnuintl so that libgnuintl.so can be used + as LD_PRELOADable library on glibc systems, to provide the extra + features that the functions in the libc don't have (namely, logging). */ #undef gettext diff --git a/gettext-runtime/intl/log.c b/gettext-runtime/intl/log.c new file mode 100644 index 000000000..9c84791b9 --- /dev/null +++ b/gettext-runtime/intl/log.c @@ -0,0 +1,104 @@ +/* Log file output. + Copyright (C) 2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. */ + +/* Written by Bruno Haible . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +/* Print an ASCII string with quotes and escape sequences where needed. */ +static void +print_escaped (stream, str) + FILE *stream; + const char *str; +{ + putc ('"', stream); + for (; *str != '\0'; str++) + if (*str == '\n') + { + fputs ("\\n\"", stream); + if (str[1] == '\0') + return; + fputs ("\n\"", stream); + } + else + { + if (*str == '"' || *str == '\\') + putc ('\\', stream); + putc (*str, stream); + } + putc ('"', stream); +} + +/* Add to the log file an entry denoting a failed translation. */ +void +_nl_log_untranslated (logfilename, domainname, msgid1, msgid2, plural) + const char *logfilename; + const char *domainname; + const char *msgid1; + const char *msgid2; + int plural; +{ + static char *last_logfilename = NULL; + static FILE *last_logfile = NULL; + FILE *logfile; + + /* Can we reuse the last opened logfile? */ + if (last_logfilename == NULL || strcmp (logfilename, last_logfilename) != 0) + { + /* Close the last used logfile. */ + if (last_logfilename != NULL) + { + if (last_logfile != NULL) + { + fclose (last_logfile); + last_logfile = NULL; + } + free (last_logfilename); + last_logfilename = NULL; + } + /* Open the logfile. */ + last_logfilename = (char *) malloc (strlen (logfilename) + 1); + if (last_logfilename == NULL) + return; + strcpy (last_logfilename, logfilename); + last_logfile = fopen (logfilename, "a"); + if (last_logfile == NULL) + return; + } + logfile = last_logfile; + + fprintf (logfile, "domain "); + print_escaped (logfile, domainname); + fprintf (logfile, "\nmsgid "); + print_escaped (logfile, msgid1); + if (plural) + { + fprintf (logfile, "\nmsgid_plural "); + print_escaped (logfile, msgid2); + fprintf (logfile, "\nmsgstr[0] \"\"\n"); + } + else + fprintf (logfile, "\nmsgstr \"\"\n"); + putc ('\n', logfile); +} diff --git a/gettext-runtime/m4/ChangeLog b/gettext-runtime/m4/ChangeLog index 42f38f096..cf4b06284 100644 --- a/gettext-runtime/m4/ChangeLog +++ b/gettext-runtime/m4/ChangeLog @@ -1,3 +1,8 @@ +2003-02-16 Bruno Haible + + * gettext.m4 (AM_GNU_GETTEXT): Move INTLOBJS to backward compatibility + section. + 2003-02-12 Bruno Haible Restructure gettext package. diff --git a/gettext-runtime/m4/gettext.m4 b/gettext-runtime/m4/gettext.m4 index 2348d6253..1ac36a85c 100644 --- a/gettext-runtime/m4/gettext.m4 +++ b/gettext-runtime/m4/gettext.m4 @@ -17,7 +17,7 @@ dnl They are *not* in the public domain. dnl Authors: dnl Ulrich Drepper , 1995-2000. -dnl Bruno Haible , 2000-2002. +dnl Bruno Haible , 2000-2003. dnl Macro to add for using GNU gettext. @@ -236,7 +236,6 @@ return (int) gettext ("")]ifelse([$2], [need-ngettext], [ + (int) ngettext ("", if test "$nls_cv_use_gnu_gettext" = "yes"; then dnl Mark actions used to generate GNU NLS library. - INTLOBJS="\$(GETTOBJS)" BUILD_INCLUDED_LIBINTL=yes USE_INCLUDED_LIBINTL=yes LIBINTL="ifelse([$3],[],\${top_builddir}/intl,[$3])/libintl.[]gt_libtool_suffix_prefix[]a $LIBICONV" @@ -308,7 +307,6 @@ return (int) gettext ("")]ifelse([$2], [need-ngettext], [ + (int) ngettext ("", AC_SUBST(BUILD_INCLUDED_LIBINTL) AC_SUBST(USE_INCLUDED_LIBINTL) AC_SUBST(CATOBJEXT) - AC_SUBST(INTLOBJS) dnl For backward compatibility. Some configure.ins may be using this. nls_cv_header_intl= @@ -326,6 +324,12 @@ return (int) gettext ("")]ifelse([$2], [need-ngettext], [ + (int) ngettext ("", GENCAT=gencat AC_SUBST(GENCAT) + dnl For backward compatibility. Some Makefiles may be using this. + if test "$USE_INCLUDED_LIBINTL" = yes; then + INTLOBJS="\$(GETTOBJS)" + fi + AC_SUBST(INTLOBJS) + dnl Enable libtool support if the surrounding package wishes it. INTL_LIBTOOL_SUFFIX_PREFIX=gt_libtool_suffix_prefix AC_SUBST(INTL_LIBTOOL_SUFFIX_PREFIX) diff --git a/gettext-tools/doc/ChangeLog b/gettext-tools/doc/ChangeLog index 5b125640e..4517026ec 100644 --- a/gettext-tools/doc/ChangeLog +++ b/gettext-tools/doc/ChangeLog @@ -1,3 +1,7 @@ +2003-02-16 Bruno Haible + + * gettext.texi (Prioritizing messages): New section. + 2003-02-16 Bruno Haible * gettext.texi (po/LINGUAS): Document en@quot and en@boldquot. diff --git a/gettext-tools/doc/gettext.texi b/gettext-tools/doc/gettext.texi index f1aa312a5..29d06fc13 100644 --- a/gettext-tools/doc/gettext.texi +++ b/gettext-tools/doc/gettext.texi @@ -115,7 +115,7 @@ by the Foundation. @node Top, Introduction, (dir), (dir) @top GNU @code{gettext} utilities -This manual document the GNU gettext tools and the GNU libintl library, +This manual documents the GNU gettext tools and the GNU libintl library, version @value{VERSION}. @menu @@ -264,6 +264,7 @@ The Translator's View * Discussions:: Discussions * Organization:: Organization * Information Flow:: Information Flow +* Prioritizing messages:: How to find which messages to translate first Organization @@ -5369,6 +5370,7 @@ Solaris is not the only system having @code{gettext}. * Discussions:: Discussions * Organization:: Organization * Information Flow:: Information Flow +* Prioritizing messages:: How to find which messages to translate first @end menu @node Trans Intro 0, Trans Intro 1, Translators, Translators @@ -5524,10 +5526,11 @@ the GNU Library General Public License, when they do not want to make their program free, or want other kinds of freedom. The simplest answer is ``normally not''. -The GNU @code{gettext} library, i.e. the contents of @code{libintl}, -is covered by the GNU Library General Public License. The rest of -the GNU @code{gettext} package is covered by the GNU General Public -License. +The @code{gettext-runtime} part of GNU @code{gettext}, i.e. the +contents of @code{libintl}, is covered by the GNU Library General Public +License. The @code{gettext-tools} part of GNU @code{gettext}, i.e. the +rest of the GNU @code{gettext} package, is covered by the GNU General +Public License. The mere marking of localizable strings in a package, or conditional inclusion of a few lines for initialization, is not really including @@ -5740,7 +5743,7 @@ run, each team should run its own list, from within their country. There also should be some central list to which all teams could subscribe as they see fit, as long as each team is represented in it. -@node Information Flow, , Organization, Translators +@node Information Flow, Prioritizing messages, Organization, Translators @section Information Flow There will surely be some discussion about this messages after the @@ -5778,6 +5781,116 @@ I sent a proposal for a fast and flexible format, but it is not receiving acceptance yet by the GNU deciders. I'll tell you when I have more information about this. +@node Prioritizing messages, , Information Flow, Translators +@section Prioritizing messages: How to determine which messages to translate first + +A translator sometimes has only a limited amount of time per week to +spend on a package, and some packages have quite large message catalogs +(over 1000 messages). Therefore she wishes to translate the messages +first that are the most visible to the user, or that occur most frequently. +This section describes how to determine these "most urgent" messages. +It also applies to determine the "next most urgent" messages after the +message catalog has already been partially translated. + +In a first step, she uses the programs like a user would do. While she +does this, the GNU @code{gettext} library logs into a file the not yet +translated messages for which a translation was requested from the program. + +In a second step, she uses the PO mode to translate precisely this set +of messages. + +@vindex GETTEXT_LOG_UNTRANSLATED@r{, environment variable} +Here a more details. The GNU @code{libintl} library (but not the +corresponding functions in GNU @code{libc}) supports an environment variable +@code{GETTEXT_LOG_UNTRANSLATED}. The GNU @code{libintl} library will +log into this file the messages for which @code{gettext()} and related +functions couldn't find the translation. If the file doesn't exist, it +will be created as needed. On systems with GNU @code{libc} a shared library +@samp{preloadable_libintl.so} is provided that can be used with the ELF +@samp{LD_PRELOAD} mechanism. + +So, in the first step, the translator uses these commands on systems with +GNU @code{libc}: + +@smallexample +$ LD_PRELOAD=/usr/local/lib/preloadable_libintl.so +$ export LD_PRELOAD +$ GETTEXT_LOG_UNTRANSLATED=$HOME/gettextlogused +$ export GETTEXT_LOG_UNTRANSLATED +@end smallexample + +@noindent +and these commands on other systems: + +@smallexample +$ GETTEXT_LOG_UNTRANSLATED=$HOME/gettextlogused +$ export GETTEXT_LOG_UNTRANSLATED +@end smallexample + +Then she uses and peruses the programs. (It is a good and recommended +practice to use the programs for which you provide translations: it +gives you the needed context.) When done, she removes the environment +variables: + +@smallexample +$ unset LD_PRELOAD +$ unset GETTEXT_LOG_UNTRANSLATED +@end smallexample + +The second step starts with removing duplicates: + +@smallexample +$ msguniq $HOME/gettextlogused > missing.po +@end smallexample + +The result is a PO file, but needs some preprocessing before the Emacs PO +mode can be used with it. First, it is a multi-domain PO file, containing +messages from many translation domains. Second, it lacks all translator +comments and source references. Here is how to get a list of the affected +translation domains: + +@smallexample +$ sed -n -e 's,^domain "\(.*\)"$,\1,p' < missing.po | sort | uniq +@end smallexample + +Then the translator can handle the domains one by one. For simplicity, +let's use environment variables to denote the language, domain and source +package. + +@smallexample +$ lang=nl # your language +$ domain=coreutils # the name of the domain to be handled +$ package=/usr/src/gnu/coreutils-4.5.4 # the package where it comes from +@end smallexample + +She takes the latest copy of @file{$lang.po} from the Translation Project, +or from the package (in most cases, @file{$package/po/$lang.po}), or +creates a fresh one if she's the first translator (see @ref{Creating}). +She then uses the following commands to mark the not urgent messages as +"obsolete". (This doesn't mean that these messages - translated and +untranslated ones - will go away. It simply means that Emacs PO mode +will ignore them in the following editing session.) + +@smallexample +$ msggrep --domain=$domain missing.po | grep -v '^domain' \ + > $domain-missing.po +$ msgattrib --set-obsolete --ignore-file $domain-missing.po $domain.$lang.po \ + > $domain.$lang-urgent.po +@end smallexample + +The she translates @file{$domain.$lang-urgent.po} by use of Emacs PO mode. +(FIXME: I don't know whether @code{KBabel} and @code{gtranslator} also +preserve obsolete messages, as they should.) +Finally she restores the not urgent messages (with their earlier +translations, for those which were already translated) through this command: + +@smallexample +$ msgmerge --no-fuzzy-matching $domain.$lang-urgent.po $package/po/$domain.pot \ + > $domain.$lang.po +@end smallexample + +Then she can submit @file{$domain.$lang.po} and proceed to the next domain. + @node Maintainers, Programming Languages, Translators, Top @chapter The Maintainer's View @cindex package maintainer's view of @code{gettext}