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