bug-gnu-utils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Bug 249431] gettext should convert untranslated non-ASCII msgids in


From: Bruno Haible
Subject: Re: [Bug 249431] gettext should convert untranslated non-ASCII msgids into locale encoding
Date: Wed, 28 Feb 2007 13:15:10 +0100
User-agent: KMail/1.5.4

https://bugzilla.novell.com/show_bug.cgi?id=249431

------- Comment #4 from address@hidden -------
The function gettext() is now standardized by the LSB, see
  
http://www.linux-foundation.org/spec/booksets/LSB-Core-generic/LSB-Core-generic/baselib-gettext.html
therefore there is not much room for changing its behaviour.

But anyway, what does it mean to support non-ASCII msgids?

  1) You must provide a way for handling the case that the user's locale
     encoding cannot represent the non-ASCII characters. Let's take the
     example from the BR you mentioned:
       https://bugzilla.novell.com/show_bug.cgi?id=248859
     (funny bug number, when it's about encodings, by the way :-))

       gettext ("St Dévote Day")

     The user certainly wishes to see "St Devote Day" then, i.e. the
     transliteration of glibc and libiconv could do it. But in more
     general cases, this does not work any more. So IMO the programmer
     must jump in and provide the ASCII equivalent too:

       gettext_na ("St Devote Day", "St Dévote Day")

  2) The argument to gettext is used as a key into the hash table in the .mo
     file. It's a hash table so that it's fast.

     If you use "St Dévote Day" as gettext argument, you have
       - to convert this string to the .mo file's text encoding first, using
         iconv(), [you cannot reliably enforce that all .mo files are in
         UTF-8 encoding],
       - to specify globally the source encoding, for example, through a new
         hypothetical function call
               bind_textdomain_source_codeset ("ISO-8859-1");

     If you use "St Devote Day" as gettext argument, you have none of these
     two problems.

  3) If gettext returns the msgid, i.e. if the message was not translated,
     you have to convert it to the locale encoding yourself. Again, you
     have to specify globally the source encoding, for example, through a
     hypothetical function call
               bind_textdomain_source_codeset ("ISO-8859-1");
     Additionally, you don't want a memory leak here, when the same call
     is made repeatedly. So you need to cache the result for later reuse.

You can get rid of the need for bind_textdomain_source_codeset if you
assume that the source code is in UTF-8. So what you end up with is a
user-defined function

/* Return the localization of a string whose original writing is not ASCII.
   MSGID_UTF8 is the real string, written in UTF-8 with octal or hexadecimal
   escape sequences.  MSGID_ASCII is a fallback written only with ASCII
   characters.  */

const char *
gettext_utf8 (const char *msgid_ascii, const char *msgid_utf8)
{
  /* See whether there is a translation.   */
  const char *translation = gettext (msgid_ascii);

  if (translation == msgid_ascii)
    {
      /* Access a cache here.  A little homework.  */

      /* locale_charset, c_strcasecmp, xstr_iconv are defined in gnulib.  */
      const char *locale_code = locale_charset ();
      if (c_strcasecmp (locale_code, "UTF-8") == 0)
        translation = msgid_utf8;
      else
        {
#if HAVE_ICONV
          const char *converted = xstr_iconv (msgid_utf8, "UTF-8", locale_code);
          if (converted == NULL)
            {
# if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2) || __GLIBC__ > 2 \
     || _LIBICONV_VERSION >= 0x0105
              size_t len = strlen (locale_code);
              char *locale_code_translit = XNMALLOC (len + 10 + 1, char);
              memcpy (locale_code_translit, locale_code, len);
              memcpy (locale_code_translit + len, "//TRANSLIT", 10 + 1);
              converted =
                xstr_iconv (msgid_utf8, "UTF-8", locale_code_translit);
              free (locale_code_translit);
# endif
              if (converted == NULL)
                {
                  /* Use msgid_ascii as a fallback.  */
                  converted = msgid_ascii;
                }
            }
          translation = converted;
#else
          translation = msgid_ascii;
#endif
        }
      /* Store the translation in the cache.  Homework part 2.  */
    }

  return translation;
}

Finally, you make this function available to xgettext by putting this into
po/Makevars:

XGETTEXT_OPTIONS = \
  --keyword=gettext_utf8:1 --flag=gettext_utf8:1:pass-c-format





reply via email to

[Prev in Thread] Current Thread [Next in Thread]