This is the mail archive of the libc-alpha@sourceware.cygnus.com mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

gettext output character set



Hi Ulrich,

Some time ago, we discussed the problem how the character set encoding to
which gettext() converts its translations could be made controllable by
the user. You agreed that it would be nice to have a function

  bind_textdomain_codeset (const char *domain_name, const char *encoding)

For example, Unicode aware applications like KDE or Perl-5.6 will want
the result in UTF-8. (For KDE: so they can call QString::fromUtf8. For
Perl-5.6: because the internal string representation in Perl is UTF-8.)
They can then call

  bind_textdomain_codeset ("my-program", "UTF-8").

Here is a patch which implements this. The implementation of
bind_textdomain_codeset is very similar to bindtextdomain, but without
introducing duplicated code. It stores the desired encoding in the same
'struct binding'. When a message catalog is opened, it is passed down
from dcigettext to _nl_find_domain and _nl_make_l10nflist, which then
store it in the 'struct loaded_l10nfile'. The function _nl_load_domain
then only needs to look up that desired encoding in the 'struct
loaded_l10nfile'.

On non-glibc systems with iconv, the locale's encoding is determined
by a call to the function locale_charset(), which is defined in
sh-utils-2.0g/lib/; this function is an autoconf'ed wrapper around
"nl_langinfo(CODESET)". It is not included in the patch below because
it is of no use inside glibc.


2000-04-29  Bruno Haible  <clisp.cons.org>

	* intl/libintl.h (bind_textdomain_codeset): New declaration.
	* intl/bindtextdom.c (set_binding_values): New function.
	(bindtextdomain): Call it.
	(bind_textdomain_codeset): New function.
	* intl/dcigettext.c (dcigettext): Pass binding to _nl_find_domain.
	(free_mem): Free each binding's codeset.
	* intl/gettextP.h (struct binding): Add codeset field.
	(_nl_find_domain): Add domainbinding argument.
	* intl/finddomain.c (_nl_find_domain): Add domainbinding argument.
	Pass it to _nl_make_l10nflist.
	* intl/loadinfo.h (struct loaded_l10nfile): Add domainbinding field.
	(_nl_make_l10nflist): Add domainbinding argument.
	* intl/l10nflist.c (_nl_make_l10nflist): Add domainbinding argument.
	* intl/loadmsgcat.c (_nl_load_domain): Look at the domainbinding's
	codeset when determining outcharset. If !_LIBC && HAVE_ICONV, call
	locale_charset().
	* manual/message.texi: New node "Charset conversion in gettext".

*** intl/libintl.h.bak	Sun Apr 23 16:43:51 2000
--- intl/libintl.h	Sat Apr 29 10:10:59 2000
***************
*** 78,83 ****
--- 78,88 ----
  extern char *bindtextdomain (__const char *__domainname,
  			     __const char *__dirname) __THROW;
  
+ /* Specify the character encoding in which the messages from the
+    DOMAINNAME message catalog will be returned.  */
+ extern char *bind_textdomain_codeset (__const char *__domainname,
+ 				      __const char *__codeset) __THROW;
+ 
  
  /* Optimized version of the function above.  */
  #if defined __OPTIMIZE__
*** intl/bindtextdom.c.bak	Sun Apr 23 18:10:05 2000
--- intl/bindtextdom.c	Sat Apr 29 23:50:10 2000
***************
*** 75,103 ****
     prefix.  So we have to make a difference here.  */
  #ifdef _LIBC
  # define BINDTEXTDOMAIN __bindtextdomain
  # ifndef strdup
  #  define strdup(str) __strdup (str)
  # endif
  #else
  # define BINDTEXTDOMAIN bindtextdomain__
  #endif
  
! /* Specify that the DOMAINNAME message catalog will be found
!    in DIRNAME rather than in the system locale data base.  */
! char *
! BINDTEXTDOMAIN (domainname, dirname)
       const char *domainname;
!      const char *dirname;
  {
    struct binding *binding;
!   char *result;
  
    /* Some sanity checks.  */
    if (domainname == NULL || domainname[0] == '\0')
!     return NULL;
  
    __libc_rwlock_wrlock (_nl_state_lock);
  
    for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
      {
        int compare = strcmp (domainname, binding->domainname);
--- 75,118 ----
     prefix.  So we have to make a difference here.  */
  #ifdef _LIBC
  # define BINDTEXTDOMAIN __bindtextdomain
+ # define BIND_TEXTDOMAIN_CODESET __bind_textdomain_codeset
  # ifndef strdup
  #  define strdup(str) __strdup (str)
  # endif
  #else
  # define BINDTEXTDOMAIN bindtextdomain__
+ # define BIND_TEXTDOMAIN_CODESET bind_textdomain_codeset__
  #endif
  
! /* Specifies the directory name *DIRNAMEP and the output codeset *CODESETP
!    to be used for the DOMAINNAME message catalog.
!    If *DIRNAMEP or *CODESETP is NULL, the corresponding attribute is not
!    modified, only the current value is returned.
!    If DIRNAMEP or CODESETP is NULL, the corresponding attribute is neither
!    modified nor returned.  */
! static void
! set_binding_values (domainname, dirnamep, codesetp)
       const char *domainname;
!      const char **dirnamep;
!      const char **codesetp;
  {
    struct binding *binding;
!   int modified;
  
    /* Some sanity checks.  */
    if (domainname == NULL || domainname[0] == '\0')
!     {
!       if (dirnamep)
! 	*dirnamep = NULL;
!       if (codesetp)
! 	*codesetp = NULL;
!       return;
!     }
  
    __libc_rwlock_wrlock (_nl_state_lock);
  
+   modified = 0;
+ 
    for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
      {
        int compare = strcmp (domainname, binding->domainname);
***************
*** 112,151 ****
  	}
      }
  
!   if (dirname == NULL)
!     /* The current binding has be to returned.  */
!     result = binding == NULL ? (char *) _nl_default_dirname : binding->dirname;
!   else if (binding != NULL)
      {
!       /* The domain is already bound.  If the new value and the old
! 	 one are equal we simply do nothing.  Otherwise replace the
! 	 old binding.  */
!       result = binding->dirname;
!       if (strcmp (dirname, result) != 0)
  	{
! 	  if (strcmp (dirname, _nl_default_dirname) == 0)
! 	    result = (char *) _nl_default_dirname;
  	  else
  	    {
  #if defined _LIBC || defined HAVE_STRDUP
! 	      result = strdup (dirname);
  #else
! 	      size_t len = strlen (dirname) + 1;
! 	      result = (char *) malloc (len);
! 	      if (result != NULL)
! 		memcpy (result, dirname, len);
  #endif
  	    }
  
! 	  if (result != NULL)
  	    {
! 	      if (binding->dirname != _nl_default_dirname)
! 		free (binding->dirname);
  
! 	      binding->dirname = result;
  	    }
  	}
      }
    else
      {
        /* We have to create a new binding.  */
--- 127,222 ----
  	}
      }
  
!   if (binding != NULL)
      {
!       if (dirnamep)
  	{
! 	  const char *dirname = *dirnamep;
! 
! 	  if (dirname == NULL)
! 	    /* The current binding has be to returned.  */
! 	    *dirnamep = binding->dirname;
  	  else
  	    {
+ 	      /* The domain is already bound.  If the new value and the old
+ 		 one are equal we simply do nothing.  Otherwise replace the
+ 		 old binding.  */
+ 	      char *result = binding->dirname;
+ 	      if (strcmp (dirname, result) != 0)
+ 		{
+ 		  if (strcmp (dirname, _nl_default_dirname) == 0)
+ 		    result = (char *) _nl_default_dirname;
+ 		  else
+ 		    {
  #if defined _LIBC || defined HAVE_STRDUP
! 		      result = strdup (dirname);
  #else
! 		      size_t len = strlen (dirname) + 1;
! 		      result = (char *) malloc (len);
! 		      if (__builtin_expect (result != NULL, 1))
! 			memcpy (result, dirname, len);
  #endif
+ 		    }
+ 
+ 		  if (__builtin_expect (result != NULL, 1))
+ 		    {
+ 		      if (binding->dirname != _nl_default_dirname)
+ 			free (binding->dirname);
+ 
+ 		      binding->dirname = result;
+ 		      modified = 1;
+ 		    }
+ 		}
+ 	      *dirnamep = result;
  	    }
+ 	}
  
!       if (codesetp)
! 	{
! 	  const char *codeset = *codesetp;
! 
! 	  if (codeset == NULL)
! 	    /* The current binding has be to returned.  */
! 	    *codesetp = binding->codeset;
! 	  else
  	    {
! 	      /* The domain is already bound.  If the new value and the old
! 		 one are equal we simply do nothing.  Otherwise replace the
! 		 old binding.  */
! 	      char *result = binding->codeset;
! 	      if (result == NULL || strcmp (codeset, result) != 0)
! 		{
! #if defined _LIBC || defined HAVE_STRDUP
! 		  result = strdup (codeset);
! #else
! 		  size_t len = strlen (codeset) + 1;
! 		  result = (char *) malloc (len);
! 		  if (__builtin_expect (result != NULL, 1))
! 		    memcpy (result, codeset, len);
! #endif
  
! 		  if (__builtin_expect (result != NULL, 1))
! 		    {
! 		      if (binding->codeset != NULL)
! 			free (binding->codeset);
! 
! 		      binding->codeset = result;
! 		      modified = 1;
! 		    }
! 		}
! 	      *codesetp = result;
  	    }
  	}
      }
+   else if ((dirnamep == NULL || *dirnamep == NULL)
+ 	   && (codesetp == NULL || *codesetp == NULL))
+     {
+       /* Simply return the default values.  */
+       if (dirnamep)
+ 	*dirnamep = _nl_default_dirname;
+       if (codesetp)
+ 	*codesetp = NULL;
+     }
    else
      {
        /* We have to create a new binding.  */
***************
*** 153,213 ****
        struct binding *new_binding =
  	(struct binding *) malloc (sizeof (*new_binding) + len);
  
!       if (new_binding == NULL)
! 	result = NULL;
!       else
  	{
! 	  memcpy (new_binding->domainname, domainname, len);
  
! 	  if (strcmp (dirname, _nl_default_dirname) == 0)
! 	    result = new_binding->dirname = (char *) _nl_default_dirname;
  	  else
  	    {
  #if defined _LIBC || defined HAVE_STRDUP
! 	      result = new_binding->dirname = strdup (dirname);
  #else
! 	      len = strlen (dirname) + 1;
! 	      result = new_binding->dirname = (char *) malloc (len);
! 	      if (result != NULL)
! 		memcpy (new_binding->dirname, dirname, len);
  #endif
  	    }
  	}
  
!       if (result != NULL)
  	{
! 	  /* Now enqueue it.  */
! 	  if (_nl_domain_bindings == NULL
! 	      || strcmp (domainname, _nl_domain_bindings->domainname) < 0)
! 	    {
! 	      new_binding->next = _nl_domain_bindings;
! 	      _nl_domain_bindings = new_binding;
! 	    }
! 	  else
  	    {
! 	      binding = _nl_domain_bindings;
! 	      while (binding->next != NULL
! 		     && strcmp (domainname, binding->next->domainname) > 0)
! 		binding = binding->next;
  
! 	      new_binding->next = binding->next;
! 	      binding->next = new_binding;
  	    }
  	}
-       else if (new_binding != NULL)
- 	free (new_binding);
      }
  
!   /* For a succesful call we flush the caches.  */
!   if (result != NULL)
      ++_nl_msg_cat_cntr;
  
    __libc_rwlock_unlock (_nl_state_lock);
  
!   return result;
  }
  
  #ifdef _LIBC
! /* Alias for function name in GNU C Library.  */
  weak_alias (__bindtextdomain, bindtextdomain);
  #endif
--- 224,363 ----
        struct binding *new_binding =
  	(struct binding *) malloc (sizeof (*new_binding) + len);
  
!       if (__builtin_expect (new_binding == NULL, 0))
! 	goto failed;
! 
!       memcpy (new_binding->domainname, domainname, len);
! 
!       if (dirnamep)
  	{
! 	  const char *dirname = *dirnamep;
  
! 	  if (dirname == NULL)
! 	    /* The default value.  */
! 	    dirname = _nl_default_dirname;
  	  else
  	    {
+ 	      if (strcmp (dirname, _nl_default_dirname) == 0)
+ 		dirname = _nl_default_dirname;
+ 	      else
+ 		{
+ 		  char *result;
  #if defined _LIBC || defined HAVE_STRDUP
! 		  result = strdup (dirname);
! 		  if (__builtin_expect (result == NULL, 0))
! 		    goto failed_dirname;
  #else
! 		  size_t len = strlen (dirname) + 1;
! 		  result = (char *) malloc (len);
! 		  if (__builtin_expect (result == NULL, 0))
! 		    goto failed_dirname;
! 		  memcpy (result, dirname, len);
  #endif
+ 		  dirname = result;
+ 		}
  	    }
+ 	  *dirnamep = dirname;
+ 	  new_binding->dirname = (char *) dirname;
  	}
+       else
+ 	/* The default value.  */
+ 	new_binding->dirname = (char *) _nl_default_dirname;
  
!       if (codesetp)
  	{
! 	  const char *codeset = *codesetp;
! 
! 	  if (codeset != NULL)
  	    {
! 	      char *result;
  
! #if defined _LIBC || defined HAVE_STRDUP
! 	      result = strdup (codeset);
! 	      if (__builtin_expect (result == NULL, 0))
! 		goto failed_codeset;
! #else
! 	      size_t len = strlen (codeset) + 1;
! 	      result = (char *) malloc (len);
! 	      if (__builtin_expect (result == NULL, 0))
! 		goto failed_codeset;
! 	      memcpy (result, codeset, len);
! #endif
! 	      codeset = result;
  	    }
+ 	  *codesetp = codeset;
+ 	  new_binding->codeset = (char *) codeset;
+ 	}
+       else
+ 	new_binding->codeset = NULL;
+ 
+       /* Now enqueue it.  */
+       if (_nl_domain_bindings == NULL
+ 	  || strcmp (domainname, _nl_domain_bindings->domainname) < 0)
+ 	{
+ 	  new_binding->next = _nl_domain_bindings;
+ 	  _nl_domain_bindings = new_binding;
+ 	}
+       else
+ 	{
+ 	  binding = _nl_domain_bindings;
+ 	  while (binding->next != NULL
+ 		 && strcmp (domainname, binding->next->domainname) > 0)
+ 	    binding = binding->next;
+ 
+ 	  new_binding->next = binding->next;
+ 	  binding->next = new_binding;
+ 	}
+ 
+       modified = 1;
+ 
+       /* Here we deal with memory allocation failures.  */
+       if (0)
+ 	{
+ 	failed_codeset:
+ 	  if (new_binding->dirname != _nl_default_dirname)
+ 	    free (new_binding->dirname);
+ 	failed_dirname:
+ 	  free (new_binding);
+ 	failed:
+ 	  if (dirnamep)
+ 	    *dirnamep = NULL;
+ 	  if (codesetp)
+ 	    *codesetp = NULL;
  	}
      }
  
!   /* If we modified any binding, we flush the caches.  */
!   if (modified)
      ++_nl_msg_cat_cntr;
  
    __libc_rwlock_unlock (_nl_state_lock);
+ }
+ 
+ /* Specify that the DOMAINNAME message catalog will be found
+    in DIRNAME rather than in the system locale data base.  */
+ char *
+ BINDTEXTDOMAIN (domainname, dirname)
+      const char *domainname;
+      const char *dirname;
+ {
+   set_binding_values (domainname, &dirname, NULL);
+   return (char *) dirname;
+ }
  
! /* Specify the character encoding in which the messages from the
!    DOMAINNAME message catalog will be returned.  */
! char *
! BIND_TEXTDOMAIN_CODESET (domainname, codeset)
!      const char *domainname;
!      const char *codeset;
! {
!   set_binding_values (domainname, NULL, &codeset);
!   return (char *) codeset;
  }
  
  #ifdef _LIBC
! /* Aliases for function names in GNU C Library.  */
  weak_alias (__bindtextdomain, bindtextdomain);
+ weak_alias (__bind_textdomain_codeset, bind_textdomain_codeset);
  #endif
*** intl/dcigettext.c.bak	Sun Apr 30 12:07:59 2000
--- intl/dcigettext.c	Sun Apr 30 11:15:50 2000
***************
*** 531,537 ****
  
        /* Find structure describing the message catalog matching the
  	 DOMAINNAME and CATEGORY.  */
!       domain = _nl_find_domain (dirname, single_locale, xdomainname);
  
        if (domain != NULL)
  	{
--- 531,537 ----
  
        /* Find structure describing the message catalog matching the
  	 DOMAINNAME and CATEGORY.  */
!       domain = _nl_find_domain (dirname, single_locale, xdomainname, binding);
  
        if (domain != NULL)
  	{
***************
*** 1072,1086 ****
    struct binding *runp;
  
    for (runp = _nl_domain_bindings; runp != NULL; runp = runp->next)
!     if (runp->dirname != _nl_default_dirname)
!       /* Yes, this is a pointer comparison.  */
!       free (runp->dirname);
  
    if (_nl_current_default_domain != _nl_default_default_domain)
      /* Yes, again a pointer comparison.  */
      free ((char *) _nl_current_default_domain);
  
!   /* Remove the search tree with the know translations.  */
    __tdestroy (root, free);
  }
  
--- 1072,1090 ----
    struct binding *runp;
  
    for (runp = _nl_domain_bindings; runp != NULL; runp = runp->next)
!     {
!       if (runp->dirname != _nl_default_dirname)
! 	/* Yes, this is a pointer comparison.  */
! 	free (runp->dirname);
!       if (runp->codeset != NULL)
! 	free (runp->codeset);
!     }
  
    if (_nl_current_default_domain != _nl_default_default_domain)
      /* Yes, again a pointer comparison.  */
      free ((char *) _nl_current_default_domain);
  
!   /* Remove the search tree with the known translations.  */
    __tdestroy (root, free);
  }
  
*** intl/gettextP.h.bak	Sun Apr 30 12:07:59 2000
--- intl/gettextP.h	Sun Apr 30 00:25:13 2000
***************
*** 143,148 ****
--- 143,149 ----
  {
    struct binding *next;
    char *dirname;
+   char *codeset;
  #ifdef __GNUC__
    char domainname[0];
  #else
***************
*** 154,160 ****
  
  struct loaded_l10nfile *_nl_find_domain PARAMS ((const char *__dirname,
  						 char *__locale,
! 						 const char *__domainname))
       internal_function;
  void _nl_load_domain PARAMS ((struct loaded_l10nfile *__domain))
       internal_function;
--- 155,162 ----
  
  struct loaded_l10nfile *_nl_find_domain PARAMS ((const char *__dirname,
  						 char *__locale,
! 						 const char *__domainname,
! 					      struct binding *__domainbinding))
       internal_function;
  void _nl_load_domain PARAMS ((struct loaded_l10nfile *__domain))
       internal_function;
*** intl/finddomain.c.bak	Sun Apr 30 12:07:59 2000
--- intl/finddomain.c	Sat Apr 29 23:55:36 2000
***************
*** 67,76 ****
     established bindings.  */
  struct loaded_l10nfile *
  internal_function
! _nl_find_domain (dirname, locale, domainname)
       const char *dirname;
       char *locale;
       const char *domainname;
  {
    struct loaded_l10nfile *retval;
    const char *language;
--- 67,77 ----
     established bindings.  */
  struct loaded_l10nfile *
  internal_function
! _nl_find_domain (dirname, locale, domainname, domainbinding)
       const char *dirname;
       char *locale;
       const char *domainname;
+      struct binding *domainbinding;
  {
    struct loaded_l10nfile *retval;
    const char *language;
***************
*** 109,115 ****
       be one data set in the list of loaded domains.  */
    retval = _nl_make_l10nflist (&_nl_loaded_domains, dirname,
  			       strlen (dirname) + 1, 0, locale, NULL, NULL,
! 			       NULL, NULL, NULL, NULL, NULL, domainname, 0);
    if (retval != NULL)
      {
        /* We know something about this locale.  */
--- 110,117 ----
       be one data set in the list of loaded domains.  */
    retval = _nl_make_l10nflist (&_nl_loaded_domains, dirname,
  			       strlen (dirname) + 1, 0, locale, NULL, NULL,
! 			       NULL, NULL, NULL, NULL, NULL, domainname,
! 			       domainbinding, 0);
    if (retval != NULL)
      {
        /* We know something about this locale.  */
***************
*** 165,171 ****
    retval = _nl_make_l10nflist (&_nl_loaded_domains, dirname,
  			       strlen (dirname) + 1, mask, language, territory,
  			       codeset, normalized_codeset, modifier, special,
! 			       sponsor, revision, domainname, 1);
    if (retval == NULL)
      /* This means we are out of core.  */
      return NULL;
--- 167,174 ----
    retval = _nl_make_l10nflist (&_nl_loaded_domains, dirname,
  			       strlen (dirname) + 1, mask, language, territory,
  			       codeset, normalized_codeset, modifier, special,
! 			       sponsor, revision, domainname, domainbinding,
! 			       1);
    if (retval == NULL)
      /* This means we are out of core.  */
      return NULL;
*** intl/loadinfo.h.bak	Sun Apr 30 12:07:59 2000
--- intl/loadinfo.h	Sun Apr 30 10:30:44 2000
***************
*** 55,60 ****
--- 55,61 ----
  struct loaded_l10nfile
  {
    const char *filename;
+   struct binding *domainbinding;
    int decided;
  
    const void *data;
***************
*** 79,85 ****
  			    const char *normalized_codeset,
  			    const char *modifier, const char *special,
  			    const char *sponsor, const char *revision,
! 			    const char *filename, int do_allocate));
  
  
  extern const char *_nl_expand_alias PARAMS ((const char *name));
--- 80,87 ----
  			    const char *normalized_codeset,
  			    const char *modifier, const char *special,
  			    const char *sponsor, const char *revision,
! 			    const char *filename,
! 			    struct binding *domainbinding, int do_allocate));
  
  
  extern const char *_nl_expand_alias PARAMS ((const char *name));
*** intl/l10nflist.c.bak	Sun Apr 30 12:07:59 2000
--- intl/l10nflist.c	Sun Apr 30 10:31:28 2000
***************
*** 175,181 ****
  struct loaded_l10nfile *
  _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len, mask, language,
  		    territory, codeset, normalized_codeset, modifier, special,
! 		    sponsor, revision, filename, do_allocate)
       struct loaded_l10nfile **l10nfile_list;
       const char *dirlist;
       size_t dirlist_len;
--- 175,181 ----
  struct loaded_l10nfile *
  _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len, mask, language,
  		    territory, codeset, normalized_codeset, modifier, special,
! 		    sponsor, revision, filename, domainbinding, do_allocate)
       struct loaded_l10nfile **l10nfile_list;
       const char *dirlist;
       size_t dirlist_len;
***************
*** 189,194 ****
--- 189,195 ----
       const char *sponsor;
       const char *revision;
       const char *filename;
+      struct binding *domainbinding;
       int do_allocate;
  {
    char *abs_filename;
***************
*** 309,314 ****
--- 310,316 ----
      return NULL;
  
    retval->filename = abs_filename;
+   retval->domainbinding = domainbinding;
    retval->decided = (__argz_count (dirlist, dirlist_len) != 1
  		     || ((mask & XPG_CODESET) != 0
  			 && (mask & XPG_NORM_CODESET) != 0));
***************
*** 344,350 ****
  	    = _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1, cnt,
  				  language, territory, codeset,
  				  normalized_codeset, modifier, special,
! 				  sponsor, revision, filename, 1);
        }
    retval->successor[entries] = NULL;
  
--- 346,353 ----
  	    = _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1, cnt,
  				  language, territory, codeset,
  				  normalized_codeset, modifier, special,
! 				  sponsor, revision, filename, domainbinding,
! 				  1);
        }
    retval->successor[entries] = NULL;
  
*** intl/loadmsgcat.c.bak	Sun Apr 30 12:07:59 2000
--- intl/loadmsgcat.c	Sun Apr 30 11:35:00 2000
***************
*** 291,300 ****
  
  	  /* The output charset should normally be determined by the
  	     locale.  But sometimes the locale is not used or not correctly
! 	     set up so we provide a possibility to override this.  */
! 	  outcharset = getenv ("OUTPUT_CHARSET");
! 	  if (outcharset == NULL || outcharset[0] == '\0')
! 	    outcharset = (*_nl_current[LC_CTYPE])->values[_NL_ITEM_INDEX (CODESET)].string;
  
  # ifdef _LIBC
  	  if (__gconv_open (outcharset, charset, &domain->conv,
--- 291,319 ----
  
  	  /* The output charset should normally be determined by the
  	     locale.  But sometimes the locale is not used or not correctly
! 	     set up, so we provide a possibility for the user to override
! 	     this.  Moreover, the value specified through
! 	     bind_textdomain_codeset overrides both.  */
! 	  if (domain_file->domainbinding != NULL
! 	      && domain_file->domainbinding->codeset != NULL)
! 	    outcharset = domain_file->domainbinding->codeset;
! 	  else
! 	    {
! 	      outcharset = getenv ("OUTPUT_CHARSET");
! 	      if (outcharset == NULL || outcharset[0] == '\0')
! 		{
! # ifdef _LIBC
! 		  outcharset = (*_nl_current[LC_CTYPE])->values[_NL_ITEM_INDEX (CODESET)].string;
! # else
! #  if HAVE_ICONV
! 		  extern const char *locale_charset (void);
! 		  outcharset = locale_charset ();
! 		  if (outcharset == NULL)
! 		    outcharset = "";
! #  endif
! # endif
! 		}
! 	    }
  
  # ifdef _LIBC
  	  if (__gconv_open (outcharset, charset, &domain->conv,
*** manual/message.texi.bak	Fri Mar 17 00:10:46 2000
--- manual/message.texi	Sun Apr 30 15:57:28 2000
***************
*** 764,776 ****
  categories:
  
  @menu
! * Translation with gettext::    What has to be done to translate a message.
! * Locating gettext catalog::    How to determine which catalog to be used.
! * Advanced gettext functions::  Additional functions for more complicated
!                                  situations.
! * GUI program problems::        How to use @code{gettext} in GUI programs.
! * Using gettextized software::  The possibilities of the user to influence
!                                  the way @code{gettext} works.
  @end menu
  
  @node Translation with gettext
--- 764,778 ----
  categories:
  
  @menu
! * Translation with gettext::       What has to be done to translate a message.
! * Locating gettext catalog::       How to determine which catalog to be used.
! * Advanced gettext functions::     Additional functions for more complicated
!                                     situations.
! * Charset conversion in gettext::  How to specify the output character set
!                                     @code{gettext} uses.
! * GUI program problems::           How to use @code{gettext} in GUI programs.
! * Using gettextized software::     The possibilities of the user to influence
!                                     the way @code{gettext} works.
  @end menu
  
  @node Translation with gettext
***************
*** 1045,1051 ****
  variable.
  
  The @code{bindtextdomain} function can be used several times and if the
! @var{domainname} argument is different the previously bounded domains
  will not be overwritten.
  
  If the program which wish to use @code{bindtextdomain} at some point of
--- 1047,1053 ----
  variable.
  
  The @code{bindtextdomain} function can be used several times and if the
! @var{domainname} argument is different the previously bound domains
  will not be overwritten.
  
  If the program which wish to use @code{bindtextdomain} at some point of
***************
*** 1359,1364 ****
--- 1361,1412 ----
  @end table
  
  
+ @node Charset conversion in gettext
+ @subsubsection How to specify the output character set @code{gettext} uses
+ 
+ @code{gettext} not only looks up a translation in a message catalog.  It
+ also converts the translation on the fly to the desired output character
+ set.  This is useful if the user is working in a different character set
+ than the translator who created the message catalog, because it avoids
+ distributing variants of message catalogs which differ only in the
+ character set.
+ 
+ The output character set is, by default, the value of @code{nl_langinfo
+ (CODESET)}, which depends on the @code{LC_CTYPE} part of the current
+ locale.  But programs which store strings in a locale independent way
+ (e.g. UTF-8) can request that @code{gettext} and related functions
+ return the translations in that encoding, by use of the
+ @code{bind_textdomain_codeset} function.
+ 
+ Note that the @var{msgid} argument to @code{gettext} is not subject to
+ character set conversion.  Also, when @code{gettext} does not find a
+ translation for @var{msgid}, it returns @var{msgid} unchanged --
+ independently of the current output character set.  It is therefore
+ recommended that all @var{msgid}s be US-ASCII strings.
+ 
+ @comment libintl.h
+ @comment GNU
+ @deftypefun {char *} bind_textdomain_codeset (const char *@var{domainname}, const char *@var{codeset})
+ The @code{bind_textdomain_codeset} function can be used to specify the
+ output character set for message catalogs for domain @var{domainname}.
+ 
+ If the @var{codeset} parameter is the null pointer,
+ @code{bind_textdomain_codeset} returns the currently selected codeset
+ for the domain with the name @var{domainname}. It returns @code{NULL} if
+ no codeset has yet been selected.
+ 
+ The @code{bind_textdomain_codeset} function can be used several times. 
+ If used multiple times with the same @var{domainname} argument, the
+ later call overrides the settings made by the earlier one.
+ 
+ The @code{bind_textdomain_codeset} function returns a pointer to a
+ string containing the name of the selected codeset.  The string is
+ allocated internally in the function and must not be changed by the
+ user.  If the system went out of core during the execution of
+ @code{bind_textdomain_codeset}, the return value is @code{NULL} and the
+ global variable @var{errno} is set accordingly.  @end deftypefun
+ 
+ 
  @node GUI program problems
  @subsubsection How to use @code{gettext} in GUI programs
  
***************
*** 1589,1595 ****
  @{
    textdomain ("test-package");
    bindtextdomain ("test-package", "/usr/local/share/locale");
!   puts (gettext ("Hello, world!");
  @}
  @end smallexample
  
--- 1637,1643 ----
  @{
    textdomain ("test-package");
    bindtextdomain ("test-package", "/usr/local/share/locale");
!   puts (gettext ("Hello, world!"));
  @}
  @end smallexample
  

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]