]> git.lyx.org Git - lyx.git/blob - src/support/Messages.cpp
If the translation fails for some reason, just return what we were
[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         static string oldLANGUAGE;
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                 oldLANGUAGE = getEnv("LANGUAGE");
143                 if (!setEnv("LANGUAGE", lang_))
144                         LYXERR(Debug::LOCALE, "\t... failed!");
145                 // However, setting LANGUAGE does nothing when the
146                 // locale is "C". Therefore we set the locale to
147                 // something that is believed to exist on most
148                 // systems. The idea is that one should be able to
149                 // load German documents even without having de_DE
150                 // installed.
151                 LYXERR(Debug::LOCALE, "Setting LC_ALL to en_US");
152                 if (!setEnv("LC_ALL", "en_US"))
153                         LYXERR(Debug::LOCALE, "\t... failed!");
154 #ifdef HAVE_LC_MESSAGES
155                 setlocale(LC_MESSAGES, "");
156 #endif
157         }
158
159         char const * m_c = m.c_str();
160         char const * trans_c = gettext(m_c);
161         docstring trans;
162         if (!trans_c) {
163                 LYXERR(Debug::LOCALE, "Undefined result from gettext for `" << m << "'.");
164                 trans = from_ascii(m);
165         } else if (trans_c == m_c) {
166                 //LYXERR(Debug::LOCALE, "Same as entered returned");
167                 trans = from_ascii(m);
168         } else {
169                 //LYXERR(Debug::LOCALE, "We got a translation");
170                 // m is actually not a char const * but ucs4 data
171                 trans = reinterpret_cast<char_type const *>(trans_c);
172         }
173
174         cleanTranslation(trans);
175
176         // Reset environment variables as they were.
177         if (!lang_.empty()) {
178                 // Reset everything as it was.
179                 LYXERR(Debug::LOCALE, "restoring LANGUAGE from " 
180                        << getEnv("LANGUAGE")
181                        << " to " << oldLANGUAGE);
182                 if (!setEnv("LANGUAGE", oldLANGUAGE))
183                         LYXERR(Debug::LOCALE, "\t... failed!");
184                 LYXERR(Debug::LOCALE, "restoring LC_ALL from " << getEnv("LC_ALL")
185                         << " to " << oldLC_ALL);
186                 if (!setEnv("LC_ALL", oldLC_ALL))
187                         LYXERR(Debug::LOCALE, "\t... failed!");
188 #ifdef HAVE_LC_MESSAGES
189                 setlocale(LC_MESSAGES, "");
190 #endif
191         }
192
193         pair<TranslationCache::iterator, bool> result =
194                 cache_.insert(make_pair(m, trans));
195
196         LASSERT(result.second, /**/);
197
198         return result.first->second;
199 }
200
201 } // namespace lyx
202
203 #else // ENABLE_NLS
204 // This is the dummy variant.
205
206 namespace lyx {
207
208 Messages::Messages(string const & l) {}
209
210 void Messages::init()
211 {
212 }
213
214
215 docstring const Messages::get(string const & m) const
216 {
217         docstring trans = from_ascii(m);
218         cleanTranslation(trans);
219         return trans;
220 }
221
222 } // namespace lyx
223
224 #endif
225
226 #if 0
227
228 -#include <locale>
229
230 namespace lyx {
231
232 // This version of the Pimpl utilizes the message capability of
233 // libstdc++ that is distributed with GNU G++.
234 class Messages::Pimpl {
235 public:
236         typedef messages<char>::catalog catalog;
237
238         Pimpl(string const & l)
239                 : lang_(l),
240                   loc_gl(lang_.c_str()),
241                   mssg_gl(use_facet<messages<char> >(loc_gl))
242         {
243                 //LYXERR("Messages: language(" << l << ") in dir(" << dir << ")");
244
245                 string const locale_dir = package().locale_dir().toFilesystemEncoding();
246                 cat_gl = mssg_gl.open(PACKAGE, loc_gl, locale_dir.c_str());
247
248         }
249
250         ~Pimpl()
251         {
252                 mssg_gl.close(cat_gl);
253         }
254
255         docstring const get(string const & msg) const
256         {
257                 return mssg_gl.get(cat_gl, 0, 0, msg);
258         }
259 private:
260         ///
261         string lang_;
262         ///
263         locale loc_gl;
264         ///
265         messages<char> const & mssg_gl;
266         ///
267         catalog cat_gl;
268 };
269
270 } // namespace lyx
271
272 #endif