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