Line data Source code
1 : /* i18n.c - gettext initialization
2 : * Copyright (C) 2007, 2010 Free Software Foundation, Inc.
3 : * Copyright (C) 2015 g10 Code GmbH
4 : *
5 : * This file is free software; you can redistribute it and/or modify
6 : * it under the terms of either
7 : *
8 : * - the GNU Lesser General Public License as published by the Free
9 : * Software Foundation; either version 3 of the License, or (at
10 : * your option) any later version.
11 : *
12 : * or
13 : *
14 : * - the GNU General Public License as published by the Free
15 : * Software Foundation; either version 2 of the License, or (at
16 : * your option) any later version.
17 : *
18 : * or both in parallel, as here.
19 : *
20 : * This file is distributed in the hope that it will be useful,
21 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 : * GNU General Public License for more details.
24 : *
25 : * You should have received a copy of the GNU General Public License
26 : * along with this program; if not, see <https://www.gnu.org/licenses/>.
27 : */
28 :
29 : #include <config.h>
30 : #ifdef HAVE_LOCALE_H
31 : #include <locale.h>
32 : #endif
33 : #ifdef HAVE_LANGINFO_CODESET
34 : #include <langinfo.h>
35 : #endif
36 :
37 : #include "util.h"
38 : #include "i18n.h"
39 :
40 :
41 : #undef USE_MSGCACHE
42 : #if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES) \
43 : && !defined(USE_SIMPLE_GETTEXT) && defined(ENABLE_NLS)
44 : # define USE_MSGCACHE 1
45 : #endif
46 :
47 :
48 : #ifdef USE_MSGCACHE
49 : /* An object to store pointers to static strings and their static
50 : translations. A linked list is not optimal but given that we only
51 : have a few dozen messages it should be acceptable. */
52 : struct msg_cache_s
53 : {
54 : struct msg_cache_s *next;
55 : const char *key;
56 : const char *value;
57 : };
58 :
59 : /* A object to store an lc_messages string and a link to the cache
60 : object. */
61 : struct msg_cache_heads_s
62 : {
63 : struct msg_cache_heads_s *next;
64 : struct msg_cache_s *cache;
65 : char lc_messages[1];
66 : };
67 :
68 : /* Out static cache of translated messages. We need this because
69 : there is no gettext API to return a translation depending on the
70 : locale. Switching the locale for each access to a translatable
71 : string seems to be too expensive. Note that this is used only for
72 : strings in gpg-agent which are passed to Pinentry. All other
73 : strings are using the regular gettext interface. Note that we can
74 : never release this memory because consumers take the result as
75 : static strings. */
76 : static struct msg_cache_heads_s *msgcache;
77 :
78 : #endif /*USE_MSGCACHE*/
79 :
80 :
81 : void
82 1717 : i18n_init (void)
83 : {
84 : #ifdef USE_SIMPLE_GETTEXT
85 : bindtextdomain (PACKAGE_GT, gnupg_localedir ());
86 : textdomain (PACKAGE_GT);
87 : #else
88 : # ifdef ENABLE_NLS
89 1717 : setlocale (LC_ALL, "" );
90 1717 : bindtextdomain (PACKAGE_GT, LOCALEDIR);
91 1717 : textdomain (PACKAGE_GT);
92 : # endif
93 : #endif
94 1717 : }
95 :
96 :
97 : /* The Assuan agent protocol requires us to transmit utf-8 strings
98 : thus we need a way to temporary switch gettext from native to
99 : utf8. */
100 : char *
101 444 : i18n_switchto_utf8 (void)
102 : {
103 : #ifdef USE_SIMPLE_GETTEXT
104 : /* Return an arbitrary pointer as true value. */
105 : return gettext_use_utf8 (1) ? (char*)(-1) : NULL;
106 : #elif defined(ENABLE_NLS)
107 444 : char *orig_codeset = bind_textdomain_codeset (PACKAGE_GT, NULL);
108 : # ifdef HAVE_LANGINFO_CODESET
109 444 : if (!orig_codeset)
110 411 : orig_codeset = nl_langinfo (CODESET);
111 : # endif
112 444 : if (orig_codeset)
113 : { /* We only switch when we are able to restore the codeset later.
114 : Note that bind_textdomain_codeset does only return on memory
115 : errors but not if a codeset is not available. Thus we don't
116 : bother printing a diagnostic here. */
117 444 : orig_codeset = xstrdup (orig_codeset);
118 444 : if (!bind_textdomain_codeset (PACKAGE_GT, "utf-8"))
119 : {
120 0 : xfree (orig_codeset);
121 0 : orig_codeset = NULL;
122 : }
123 : }
124 444 : return orig_codeset;
125 : #else
126 : return NULL;
127 : #endif
128 : }
129 :
130 : /* Switch back to the saved codeset. */
131 : void
132 444 : i18n_switchback (char *saved_codeset)
133 : {
134 : #ifdef USE_SIMPLE_GETTEXT
135 : gettext_use_utf8 (!!saved_codeset);
136 : #elif defined(ENABLE_NLS)
137 444 : if (saved_codeset)
138 : {
139 444 : bind_textdomain_codeset (PACKAGE_GT, saved_codeset);
140 444 : xfree (saved_codeset);
141 : }
142 : #else
143 : (void)saved_codeset;
144 : #endif
145 444 : }
146 :
147 :
148 : /* Gettext variant which temporary switches to utf-8 for string. */
149 : const char *
150 0 : i18n_utf8 (const char *string)
151 : {
152 0 : char *saved = i18n_switchto_utf8 ();
153 0 : const char *result = _(string);
154 0 : i18n_switchback (saved);
155 0 : return result;
156 : }
157 :
158 :
159 : /* A variant of gettext which allows the programmer to specify the
160 : locale to use for translating the message. The function assumes
161 : that utf-8 is used for the encoding. */
162 : const char *
163 155 : i18n_localegettext (const char *lc_messages, const char *string)
164 : {
165 : #if USE_MSGCACHE
166 155 : const char *result = NULL;
167 155 : char *saved = NULL;
168 : struct msg_cache_heads_s *mh;
169 : struct msg_cache_s *mc;
170 :
171 155 : if (!lc_messages)
172 155 : goto leave;
173 :
174 : /* Lookup in the cache. */
175 0 : for (mh = msgcache; mh; mh = mh->next)
176 0 : if (!strcmp (mh->lc_messages, lc_messages))
177 0 : break;
178 0 : if (mh)
179 : {
180 : /* A cache entry for this local exists - find the string.
181 : Because the system is designed for static strings it is
182 : sufficient to compare the pointers. */
183 0 : for (mc = mh->cache; mc; mc = mc->next)
184 0 : if (mc->key == string)
185 : {
186 : /* Cache hit. */
187 0 : result = mc->value;
188 0 : goto leave;
189 : }
190 : }
191 :
192 : /* Cached miss. Change the locale, translate, reset locale. */
193 0 : saved = setlocale (LC_MESSAGES, NULL);
194 0 : if (!saved)
195 0 : goto leave;
196 0 : saved = xtrystrdup (saved);
197 0 : if (!saved)
198 0 : goto leave;
199 0 : if (!setlocale (LC_MESSAGES, lc_messages))
200 0 : goto leave;
201 :
202 0 : bindtextdomain (PACKAGE_GT, LOCALEDIR);
203 0 : result = gettext (string);
204 0 : setlocale (LC_MESSAGES, saved);
205 0 : bindtextdomain (PACKAGE_GT, LOCALEDIR);
206 :
207 : /* Cache the result. */
208 0 : if (!mh)
209 : {
210 : /* First use of this locale - create an entry. */
211 0 : mh = xtrymalloc (sizeof *mh + strlen (lc_messages));
212 0 : if (!mh)
213 0 : goto leave;
214 0 : strcpy (mh->lc_messages, lc_messages);
215 0 : mh->cache = NULL;
216 0 : mh->next = msgcache;
217 0 : msgcache = mh;
218 : }
219 0 : mc = xtrymalloc (sizeof *mc);
220 0 : if (!mc)
221 0 : goto leave;
222 0 : mc->key = string;
223 0 : mc->value = result;
224 0 : mc->next = mh->cache;
225 0 : mh->cache = mc;
226 :
227 : leave:
228 155 : xfree (saved);
229 155 : return result? result : _(string);
230 :
231 : #else /*!USE_MSGCACHE*/
232 :
233 : (void)lc_messages;
234 : return _(string);
235 :
236 : #endif /*!USE_MSGCACHE*/
237 : }
|