]> git.lyx.org Git - lyx.git/blob - src/support/Messages.cpp
VCS: Handle one boundary condition.
[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/Package.h"
18 #include "support/unicode.h"
19
20 #include "support/lassert.h"
21
22 #include <cerrno>
23
24 using namespace std;
25
26 namespace {
27
28 using lyx::docstring;
29 using lyx::from_ascii;
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 }
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 void Messages::setDefaultLanguage()
71 {
72         char * env_lang[5] = {"LANGUAGE", "LC_ALL", "LC_MESSAGES", "LC_MESSAGE",
73                 "LANG"};
74         for (size_t i = 0; i != 5; ++i) {
75                 string const lang = getEnv(env_lang[i]);
76                 if (lang.empty())
77                         continue;
78                 Messages::main_lang_ = lang;
79                 return;
80         }
81         // Not found!
82         LYXERR(Debug::LOCALE, "Default language not found!");
83 }
84
85
86 // Instanciate static member.
87 string Messages::main_lang_;
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 docstring const Messages::get(string const & m) const
126 {
127         if (m.empty())
128                 return docstring();
129
130         // Look for the translated string in the cache.
131         TranslationCache::iterator it = cache_.find(m);
132         if (it != cache_.end())
133                 return it->second;
134
135         // The string was not found, use gettext to generate it
136         static string oldLC_ALL;
137         if (!lang_.empty()) {
138                 oldLC_ALL = getEnv("LC_ALL");
139                 // This GNU extension overrides any language locale
140                 // wrt gettext.
141                 LYXERR(Debug::LOCALE, "Setting LANGUAGE to " << lang_);
142                 if (!setEnv("LANGUAGE", lang_))
143                         LYXERR(Debug::LOCALE, "\t... failed!");
144                 // However, setting LANGUAGE does nothing when the
145                 // locale is "C". Therefore we set the locale to
146                 // something that is believed to exist on most
147                 // systems. The idea is that one should be able to
148                 // load German documents even without having de_DE
149                 // installed.
150                 LYXERR(Debug::LOCALE, "Setting LC_ALL to en_US");
151                 if (!setEnv("LC_ALL", "en_US"))
152                         LYXERR(Debug::LOCALE, "\t... failed!");
153 #ifdef HAVE_LC_MESSAGES
154                 setlocale(LC_MESSAGES, "");
155 #endif
156         }
157
158         char const * m_c = m.c_str();
159         char const * trans_c = gettext(m_c);
160         docstring trans;
161         if (!trans_c)
162                 LYXERR(Debug::LOCALE, "Undefined result from gettext");
163         else if (trans_c == m_c) {
164                 //LYXERR(Debug::LOCALE, "Same as entered returned");
165                 trans = from_ascii(m);
166         } else {
167                 //LYXERR(Debug::LOCALE, "We got a translation");
168                 // m is actually not a char const * but ucs4 data
169                 trans = reinterpret_cast<char_type const *>(trans_c);
170         }
171
172         cleanTranslation(trans);
173
174         // Reset environment variables as they were.
175         if (!lang_.empty()) {
176                 // Reset everything as it was.
177                 LYXERR(Debug::LOCALE, "restoring LANGUAGE from " << getEnv("LANGUAGE")
178                         << " to " << main_lang_);
179                 if (!setEnv("LANGUAGE", main_lang_))
180                         LYXERR(Debug::LOCALE, "\t... failed!");
181                 LYXERR(Debug::LOCALE, "restoring LC_ALL from " << getEnv("LC_ALL")
182                         << " to " << oldLC_ALL);
183                 if (!setEnv("LC_ALL", oldLC_ALL))
184                         LYXERR(Debug::LOCALE, "\t... failed!");
185 #ifdef HAVE_LC_MESSAGES
186                 setlocale(LC_MESSAGES, "");
187 #endif
188         }
189
190         pair<TranslationCache::iterator, bool> result =
191                 cache_.insert(make_pair(m, trans));
192
193         LASSERT(result.second, /**/);
194
195         return result.first->second;
196 }
197
198 } // namespace lyx
199
200 #else // ENABLE_NLS
201 // This is the dummy variant.
202
203 namespace lyx {
204
205 Messages::Messages(string const & l) {}
206
207 void Messages::init()
208 {
209 }
210
211
212 docstring const Messages::get(string const & m) const
213 {
214         docstring trans = from_ascii(m);
215         cleanTranslation(trans);
216         return trans;
217 }
218
219 } // namespace lyx
220
221 #endif
222
223 #if 0
224
225 -#include <locale>
226
227 namespace lyx {
228
229 // This version of the Pimpl utilizes the message capability of
230 // libstdc++ that is distributed with GNU G++.
231 class Messages::Pimpl {
232 public:
233         typedef messages<char>::catalog catalog;
234
235         Pimpl(string const & l)
236                 : lang_(l),
237                   loc_gl(lang_.c_str()),
238                   mssg_gl(use_facet<messages<char> >(loc_gl))
239         {
240                 //LYXERR("Messages: language(" << l << ") in dir(" << dir << ")");
241
242                 string const locale_dir = package().locale_dir().toFilesystemEncoding();
243                 cat_gl = mssg_gl.open(PACKAGE, loc_gl, locale_dir.c_str());
244
245         }
246
247         ~Pimpl()
248         {
249                 mssg_gl.close(cat_gl);
250         }
251
252         docstring const get(string const & msg) const
253         {
254                 return mssg_gl.get(cat_gl, 0, 0, msg);
255         }
256 private:
257         ///
258         string lang_;
259         ///
260         locale loc_gl;
261         ///
262         messages<char> const & mssg_gl;
263         ///
264         catalog cat_gl;
265 };
266
267 } // namespace lyx
268
269 #endif