]> git.lyx.org Git - lyx.git/blob - src/support/Messages.cpp
A first batch of potential bugs spotted by llvm/clang
[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)
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(string const & c)
116 {
117         static string locale_dir = package().locale_dir().toFilesystemEncoding();
118         string code = c;
119         // this loops at most twice
120         while (true) {
121                 string const filen = locale_dir + "/" + code 
122                         + "/LC_MESSAGES/" PACKAGE ".mo";
123                 if (FileName(filen).isReadableFile())
124                         return true;
125                 if (contains(code, '_'))
126                         code = token(code, '_', 0);
127                 else return false;
128         }
129         return false;
130
131 }
132
133 namespace {
134
135 // Trivial wrapper around gettext()
136 docstring const getText(string const & m)
137 {
138         // FIXME: gettext sometimes "forgets" the ucs4_codeset we set
139         // in init(), which leads to severe message corruption (#7371)
140         // We set it again here unconditionally. A real fix must be found!
141         LATTEST(bind_textdomain_codeset(PACKAGE, ucs4_codeset));
142
143         char const * m_c = m.c_str();
144         char const * trans_c = gettext(m_c);
145         docstring trans;
146         if (!trans_c) {
147                 LYXERR(Debug::LOCALE, "Undefined result from gettext for `" << m << "'.");
148                 trans = from_ascii(m);
149         } else if (trans_c == m_c) {
150                 //LYXERR(Debug::LOCALE, "Same as entered returned");
151                 trans = from_ascii(m);
152         } else {
153                 //LYXERR(Debug::LOCALE, "We got a translation");
154                 // m is actually not a char const * but ucs4 data
155                 trans = reinterpret_cast<char_type const *>(trans_c);
156         }
157
158         cleanTranslation(trans);
159
160         return trans;
161 }
162
163 }
164
165
166 docstring const Messages::get(string const & m) const
167 {
168         if (m.empty())
169                 return docstring();
170
171         // Look for the translated string in the cache.
172         TranslationCache::iterator it = cache_.find(m);
173         if (it != cache_.end())
174                 return it->second;
175
176         // The string was not found, use gettext to generate it
177         docstring trans;
178         if (!lang_.empty()) {
179                 // This GNU extension overrides any language locale
180                 // wrt gettext.
181                 LYXERR(Debug::LOCALE, "Setting LANGUAGE to " << lang_);
182                 EnvChanger language_chg("LANGUAGE", lang_);
183                 // However, setting LANGUAGE does nothing when the
184                 // locale is "C". Therefore we set the locale to
185                 // something that is believed to exist on most
186                 // systems. The idea is that one should be able to
187                 // load German documents even without having de_DE
188                 // installed.
189                 LYXERR(Debug::LOCALE, "Setting LC_ALL to en_US");
190                 EnvChanger lc_all_chg("LC_ALL", "en_US");
191 #ifdef HAVE_LC_MESSAGES
192                 setlocale(LC_MESSAGES, "");
193 #endif
194                 trans = getText(m);
195         } else
196                 trans = getText(m);
197                 
198
199 #ifdef HAVE_LC_MESSAGES
200         setlocale(LC_MESSAGES, "");
201 #endif
202
203         // store translation in cache
204         pair<TranslationCache::iterator, bool> result =
205                 cache_.insert(make_pair(m, trans));
206
207         LASSERT(result.second, return from_utf8(m));
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 std::string Messages::language() const
234     {
235         return string();
236     }
237
238 bool Messages::available(string const & /* c */)
239 {
240         return false;
241 }
242
243 } // namespace lyx
244
245 #endif
246
247 #if 0
248
249 -#include <locale>
250
251 namespace lyx {
252
253 // This version of the Pimpl utilizes the message capability of
254 // libstdc++ that is distributed with GNU G++.
255 class Messages::Pimpl {
256 public:
257         typedef messages<char>::catalog catalog;
258
259         Pimpl(string const & l)
260                 : lang_(l),
261                   loc_gl(lang_.c_str()),
262                   mssg_gl(use_facet<messages<char> >(loc_gl))
263         {
264                 //LYXERR("Messages: language(" << l << ") in dir(" << dir << ")");
265
266                 string const locale_dir = package().locale_dir().toFilesystemEncoding();
267                 cat_gl = mssg_gl.open(PACKAGE, loc_gl, locale_dir.c_str());
268
269         }
270
271         ~Pimpl()
272         {
273                 mssg_gl.close(cat_gl);
274         }
275
276         docstring const get(string const & msg) const
277         {
278                 return mssg_gl.get(cat_gl, 0, 0, msg);
279         }
280 private:
281         ///
282         string lang_;
283         ///
284         locale loc_gl;
285         ///
286         messages<char> const & mssg_gl;
287         ///
288         catalog cat_gl;
289 };
290
291 } // namespace lyx
292
293 #endif