]> git.lyx.org Git - lyx.git/blob - src/support/Messages.cpp
0aa29d84e0290caecd3b0e36e865c0a637c29d51
[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         char const * m_c = m.c_str();
169         char const * trans_c = gettext(m_c);
170         docstring trans;
171         if (!trans_c) {
172                 LYXERR(Debug::LOCALE, "Undefined result from gettext for `" << m << "'.");
173                 trans = from_ascii(m);
174         } else if (trans_c == m_c) {
175                 //LYXERR(Debug::LOCALE, "Same as entered returned");
176                 trans = from_ascii(m);
177         } else {
178                 //LYXERR(Debug::LOCALE, "We got a translation");
179                 // m is actually not a char const * but ucs4 data
180                 trans = reinterpret_cast<char_type const *>(trans_c);
181         }
182
183         cleanTranslation(trans);
184
185         // Reset environment variables as they were.
186         if (!lang_.empty()) {
187                 // Reset everything as it was.
188                 LYXERR(Debug::LOCALE, "restoring LANGUAGE from " 
189                        << getEnv("LANGUAGE")
190                        << " to " << oldLANGUAGE);
191                 if (!setEnv("LANGUAGE", oldLANGUAGE))
192                         LYXERR(Debug::LOCALE, "\t... failed!");
193                 LYXERR(Debug::LOCALE, "restoring LC_ALL from " << getEnv("LC_ALL")
194                         << " to " << oldLC_ALL);
195                 if (!setEnv("LC_ALL", oldLC_ALL))
196                         LYXERR(Debug::LOCALE, "\t... failed!");
197 #ifdef HAVE_LC_MESSAGES
198                 setlocale(LC_MESSAGES, "");
199 #endif
200         }
201
202         pair<TranslationCache::iterator, bool> result =
203                 cache_.insert(make_pair(m, trans));
204
205         LASSERT(result.second, /**/);
206
207         return result.first->second;
208 }
209
210 } // namespace lyx
211
212 #else // ENABLE_NLS
213 // This is the dummy variant.
214
215 namespace lyx {
216
217 Messages::Messages(string const & /* l */) {}
218
219 void Messages::init()
220 {
221 }
222
223
224 docstring const Messages::get(string const & m) const
225 {
226         docstring trans = from_ascii(m);
227         cleanTranslation(trans);
228         return trans;
229 }
230
231
232 bool Messages::available() const
233 {
234         return true;
235 }
236
237 } // namespace lyx
238
239 #endif
240
241 #if 0
242
243 -#include <locale>
244
245 namespace lyx {
246
247 // This version of the Pimpl utilizes the message capability of
248 // libstdc++ that is distributed with GNU G++.
249 class Messages::Pimpl {
250 public:
251         typedef messages<char>::catalog catalog;
252
253         Pimpl(string const & l)
254                 : lang_(l),
255                   loc_gl(lang_.c_str()),
256                   mssg_gl(use_facet<messages<char> >(loc_gl))
257         {
258                 //LYXERR("Messages: language(" << l << ") in dir(" << dir << ")");
259
260                 string const locale_dir = package().locale_dir().toFilesystemEncoding();
261                 cat_gl = mssg_gl.open(PACKAGE, loc_gl, locale_dir.c_str());
262
263         }
264
265         ~Pimpl()
266         {
267                 mssg_gl.close(cat_gl);
268         }
269
270         docstring const get(string const & msg) const
271         {
272                 return mssg_gl.get(cat_gl, 0, 0, msg);
273         }
274 private:
275         ///
276         string lang_;
277         ///
278         locale loc_gl;
279         ///
280         messages<char> const & mssg_gl;
281         ///
282         catalog cat_gl;
283 };
284
285 } // namespace lyx
286
287 #endif