common: Implement i18n_localegettext.

* common/i18n.c (msg_cache_s, msg_cache_head_s): New.
(msgcache): New.
(i18n_localegettext): Implement locale dependent lookup.
--

This is the second and final part of the change to use the gpg
provided locale for Pinentry strings.  It does not yet work on
Windows, though.

This commit should resolve
Debian-bug-id: 788983

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2015-07-01 11:49:37 +02:00
parent 816824953c
commit a65447f0d6
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
1 changed files with 104 additions and 4 deletions

View File

@ -1,5 +1,6 @@
/* i18n.c - gettext initialization
* Copyright (C) 2007, 2010 Free Software Foundation, Inc.
* Copyright (C) 2007, 2010 Free Software Foundation, Inc.
* Copyright (C) 2015 g10 Code GmbH
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of either
@ -37,6 +38,37 @@
#include "i18n.h"
/* An object to store pointers to static strings and there static
translation. A linked list is not optimal but given that we only
have a few dozen messages it should be acceptable. */
struct msg_cache_s
{
struct msg_cache_s *next;
const char *key;
const char *value;
};
/* A object to store an lc_messages string and a link to the cache
object. */
struct msg_cache_heads_s
{
struct msg_cache_heads_s *next;
struct msg_cache_s *cache;
char lc_messages[1];
};
/* Out static cache of translated messages. We need this because
there is no gettext API to return a translation depending on the
locale. Switching the locale for each access to a translatable
string seems to be too expensive. Note that this is used only for
strings in gpg-agent which are passed to Pinentry. All other
strings are using the regular gettext interface. Note that we can
never release this memory because consumers take the result as
static strings. */
static struct msg_cache_heads_s *msgcache;
void
i18n_init (void)
{
@ -117,11 +149,79 @@ i18n_utf8 (const char *string)
/* A variant of gettext which allows to specify the local to use for
translating the message. The function assumes that utf-8 is used
for the encoding. FIXME: The locale back and forth switching is
likely very expensive, thus we should consider to implement our own
cache here. */
for the encoding. */
const char *
i18n_localegettext (const char *lc_messages, const char *string)
{
#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES) \
&& !defined(USE_SIMPLE_GETTEXT) && defined(ENABLE_NLS)
const char *result = NULL;
char *saved = NULL;
struct msg_cache_heads_s *mh;
struct msg_cache_s *mc;
if (!lc_messages)
goto leave;
/* Lookup in the cache. */
for (mh = msgcache; mh; mh = mh->next)
if (!strcmp (mh->lc_messages, lc_messages))
break;
if (mh)
{
/* A cache entry for this local exists - find the string.
Because the system is designed for static strings it is
sufficient to compare the pointers. */
for (mc = mh->cache; mc; mc = mc->next)
if (mc->key == string)
{
/* Cache hit. */
result = mc->value;
goto leave;
}
}
/* Cached miss. Change the locale, translate, reset locale. */
saved = setlocale (LC_MESSAGES, NULL);
if (!saved)
goto leave;
saved = xtrystrdup (saved);
if (!saved)
goto leave;
if (!setlocale (LC_MESSAGES, lc_messages))
goto leave;
bindtextdomain (PACKAGE_GT, LOCALEDIR);
result = gettext (string);
setlocale (LC_MESSAGES, saved);
bindtextdomain (PACKAGE_GT, LOCALEDIR);
/* Cache the result. */
if (!mh)
{
/* First use of this locale - create an entry. */
mh = xtrymalloc (sizeof *mh + strlen (lc_messages));
if (!mh)
goto leave;
strcpy (mh->lc_messages, lc_messages);
mh->cache = NULL;
mh->next = msgcache;
msgcache = mh;
}
mc = xtrymalloc (sizeof *mc);
if (!mc)
goto leave;
mc->key = string;
mc->value = result;
mc->next = mh->cache;
mh->cache = mc;
leave:
xfree (saved);
return result? result : _(string);
#else /*!(HAVE_SETLOCALE && LC_MESSAGES ...)*/
(void)lc_messages;
return _(string);
#endif /*!(HAVE_SETLOCALE && LC_MESSAGES ...)*/
}