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