]> git.lyx.org Git - features.git/blob - src/support/Messages.cpp
8d85460ea6dba9e1930b181f9bd73b64f1ab9be1
[features.git] / src / support / Messages.cpp
1 /* \file Messages.cpp
2  * This file is part of LyX, the document processor.
3  * Licence details can be found in the file COPYING.
4  *
5  * \author Lars Gullik Bjønnes
6  *
7  * Full author contact details are available in file CREDITS.
8  */
9
10 #include <config.h>
11
12 #include "support/Messages.h"
13
14 #include "support/debug.h"
15 #include "support/docstring.h"
16 #include "support/environment.h"
17 #include "support/lstrings.h"
18 #include "support/Package.h"
19 #include "support/unicode.h"
20
21 #include "support/lassert.h"
22
23 #include <cerrno>
24
25 using namespace std;
26
27 namespace lyx {
28
29 // Instanciate static member.
30 string Messages::main_lang_;
31
32 void cleanTranslation(docstring & trans) 
33 {
34         /*
35           Some english words have different translations, depending on
36           context. In these cases the original string is augmented by
37           context information (e.g. "To:[[as in 'From page x to page
38           y']]" and "To:[[as in 'From format x to format y']]". This
39           means that we need to filter out everything in double square
40           brackets at the end of the string, otherwise the user sees
41           bogus messages. If we are unable to honour the request we
42           just return what we got in.
43         */
44         size_t const pos1 = trans.find(from_ascii("[["));
45         if (pos1 != docstring::npos) {
46                 size_t const pos2 = trans.find(from_ascii("]]"), pos1);
47                 if (pos2 != docstring::npos) 
48                         trans.erase(pos1, pos2 - pos1 + 2);
49         }
50 }
51
52 } // lyx
53
54
55 #ifdef ENABLE_NLS
56
57 #  ifdef HAVE_LOCALE_H
58 #    include <locale.h>
59 #  endif
60
61 #  if HAVE_GETTEXT
62 #    include <libintl.h>      // use the header already in the system *EK*
63 #  else
64 #    include "intl/libintl.h"
65 #  endif
66
67 using namespace lyx::support;
68
69 namespace lyx {
70
71 void Messages::setDefaultLanguage()
72 {
73         char const * env_lang[5] = {"LANGUAGE", "LC_ALL", "LC_MESSAGES",
74                 "LC_MESSAGE", "LANG"};
75         for (size_t i = 0; i != 5; ++i) {
76                 string const lang = getEnv(env_lang[i]);
77                 if (lang.empty())
78                         continue;
79                 Messages::main_lang_ = lang;
80                 return;
81         }
82         // Not found!
83         LYXERR(Debug::LOCALE, "Default language not found!");
84 }
85
86
87 // This version use the traditional gettext.
88 Messages::Messages(string const & l)
89         : lang_(l), warned_(false)
90 {
91         // strip off any encoding suffix, i.e., assume 8-bit po files
92         size_t i = lang_.find(".");
93         lang_ = lang_.substr(0, i);
94         LYXERR(Debug::LOCALE, "language(" << lang_ << ")");
95 }
96
97
98 void Messages::init()
99 {
100         errno = 0;
101         string const locale_dir = package().locale_dir().toFilesystemEncoding();
102         char const * c = bindtextdomain(PACKAGE, locale_dir.c_str());
103         int e = errno;
104         if (e) {
105                 LYXERR(Debug::LOCALE, "Error code: " << errno << '\n'
106                         << "Directory : " << package().locale_dir().absFileName() << '\n'
107                         << "Rtn value : " << c);
108         }
109
110         if (!bind_textdomain_codeset(PACKAGE, ucs4_codeset)) {
111                 LYXERR(Debug::LOCALE, "Error code: " << errno << '\n'
112                         << "Codeset   : " << ucs4_codeset);
113         }
114
115         textdomain(PACKAGE);
116
117         // Reset default language;
118         setDefaultLanguage();
119 }
120
121
122 bool Messages::available() const
123 {
124         string const test = languageTestString();
125         string const trans = to_utf8(get(test));
126         return !trans.empty() && trans != test;
127 }
128
129
130 docstring const Messages::get(string const & m) const
131 {
132         if (m.empty())
133                 return docstring();
134
135         // Look for the translated string in the cache.
136         TranslationCache::iterator it = cache_.find(m);
137         if (it != cache_.end())
138                 return it->second;
139
140         // The string was not found, use gettext to generate it
141         static string oldLC_ALL;
142         static string oldLANGUAGE;
143         if (!lang_.empty()) {
144                 oldLC_ALL = getEnv("LC_ALL");
145                 // This GNU extension overrides any language locale
146                 // wrt gettext.
147                 LYXERR(Debug::LOCALE, "Setting LANGUAGE to " << lang_);
148                 oldLANGUAGE = getEnv("LANGUAGE");
149                 if (!setEnv("LANGUAGE", lang_))
150                         LYXERR(Debug::LOCALE, "\t... failed!");
151                 // However, setting LANGUAGE does nothing when the
152                 // locale is "C". Therefore we set the locale to
153                 // something that is believed to exist on most
154                 // systems. The idea is that one should be able to
155                 // load German documents even without having de_DE
156                 // installed.
157                 LYXERR(Debug::LOCALE, "Setting LC_ALL to en_US");
158                 if (!setEnv("LC_ALL", "en_US"))
159                         LYXERR(Debug::LOCALE, "\t... failed!");
160 #ifdef HAVE_LC_MESSAGES
161                 setlocale(LC_MESSAGES, "");
162 #endif
163         }
164
165         // FIXME: gettext sometimes "forgets" the ucs4_codeset we set
166         // in init(), which leads to severe message corruption (#7371)
167         // We set it again here unconditionally. A real fix must be found!
168         LASSERT(bind_textdomain_codeset(PACKAGE, ucs4_codeset), /**/);
169
170         char const * m_c = m.c_str();
171         char const * trans_c = gettext(m_c);
172         docstring trans;
173         if (!trans_c) {
174                 LYXERR(Debug::LOCALE, "Undefined result from gettext for `" << m << "'.");
175                 trans = from_ascii(m);
176         } else if (trans_c == m_c) {
177                 //LYXERR(Debug::LOCALE, "Same as entered returned");
178                 trans = from_ascii(m);
179         } else {
180                 //LYXERR(Debug::LOCALE, "We got a translation");
181                 // m is actually not a char const * but ucs4 data
182                 trans = reinterpret_cast<char_type const *>(trans_c);
183         }
184
185         cleanTranslation(trans);
186
187         // Reset environment variables as they were.
188         if (!lang_.empty()) {
189                 // Reset everything as it was.
190                 LYXERR(Debug::LOCALE, "restoring LANGUAGE from " 
191                        << getEnv("LANGUAGE")
192                        << " to " << oldLANGUAGE);
193                 if (!setEnv("LANGUAGE", oldLANGUAGE))
194                         LYXERR(Debug::LOCALE, "\t... failed!");
195                 LYXERR(Debug::LOCALE, "restoring LC_ALL from " << getEnv("LC_ALL")
196                         << " to " << oldLC_ALL);
197                 if (!setEnv("LC_ALL", oldLC_ALL))
198                         LYXERR(Debug::LOCALE, "\t... failed!");
199 #ifdef HAVE_LC_MESSAGES
200                 setlocale(LC_MESSAGES, "");
201 #endif
202         }
203
204         pair<TranslationCache::iterator, bool> result =
205                 cache_.insert(make_pair(m, trans));
206
207         LASSERT(result.second, /**/);
208
209         return result.first->second;
210 }
211
212 } // namespace lyx
213
214 #else // ENABLE_NLS
215 // This is the dummy variant.
216
217 namespace lyx {
218
219 Messages::Messages(string const & /* l */) {}
220
221 void Messages::init()
222 {
223 }
224
225
226 docstring const Messages::get(string const & m) const
227 {
228         docstring trans = from_ascii(m);
229         cleanTranslation(trans);
230         return trans;
231 }
232
233
234 bool Messages::available() const
235 {
236         return false;
237 }
238
239 } // namespace lyx
240
241 #endif
242
243 #if 0
244
245 -#include <locale>
246
247 namespace lyx {
248
249 // This version of the Pimpl utilizes the message capability of
250 // libstdc++ that is distributed with GNU G++.
251 class Messages::Pimpl {
252 public:
253         typedef messages<char>::catalog catalog;
254
255         Pimpl(string const & l)
256                 : lang_(l),
257                   loc_gl(lang_.c_str()),
258                   mssg_gl(use_facet<messages<char> >(loc_gl))
259         {
260                 //LYXERR("Messages: language(" << l << ") in dir(" << dir << ")");
261
262                 string const locale_dir = package().locale_dir().toFilesystemEncoding();
263                 cat_gl = mssg_gl.open(PACKAGE, loc_gl, locale_dir.c_str());
264
265         }
266
267         ~Pimpl()
268         {
269                 mssg_gl.close(cat_gl);
270         }
271
272         docstring const get(string const & msg) const
273         {
274                 return mssg_gl.get(cat_gl, 0, 0, msg);
275         }
276 private:
277         ///
278         string lang_;
279         ///
280         locale loc_gl;
281         ///
282         messages<char> const & mssg_gl;
283         ///
284         catalog cat_gl;
285 };
286
287 } // namespace lyx
288
289 #endif