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.
$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
+2003-02-16 Bruno Haible <bruno@clisp.org>
+
+ * 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 <bruno@clisp.org>
* Makefile.in (.sin.sed): Remove rule.
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 \
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
$(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
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 \
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; \
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); \
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; \
}
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);
}
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. */
}
}
}
- /* 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);
}
/* 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
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 <libintl.h> include file. */
+ has the redirections primarily in the <libintl.h> 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
--- /dev/null
+/* 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 <bruno@clisp.org>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* 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);
+}
+2003-02-16 Bruno Haible <bruno@clisp.org>
+
+ * gettext.m4 (AM_GNU_GETTEXT): Move INTLOBJS to backward compatibility
+ section.
+
2003-02-12 Bruno Haible <bruno@clisp.org>
Restructure gettext package.
dnl Authors:
dnl Ulrich Drepper <drepper@cygnus.com>, 1995-2000.
-dnl Bruno Haible <haible@clisp.cons.org>, 2000-2002.
+dnl Bruno Haible <haible@clisp.cons.org>, 2000-2003.
dnl Macro to add for using GNU gettext.
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"
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=
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)
+2003-02-16 Bruno Haible <bruno@clisp.org>
+
+ * gettext.texi (Prioritizing messages): New section.
+
2003-02-16 Bruno Haible <bruno@clisp.org>
* gettext.texi (po/LINGUAS): Document en@quot and en@boldquot.
@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
* Discussions:: Discussions
* Organization:: Organization
* Information Flow:: Information Flow
+* Prioritizing messages:: How to find which messages to translate first
Organization
* 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
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
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
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}