]> git.lyx.org Git - lyx.git/blob - src/messages.C
Messages:
[lyx.git] / src / messages.C
1 /* \file messages.C
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 "debug.h"
13 #include "messages.h"
14 #include "support/filetools.h"
15 #include "support/environment.h"
16 #include "support/package.h"
17 #include "support/docstring.h"
18 #include "support/types.h"
19
20 #include <boost/current_function.hpp>
21 #include <boost/regex.hpp>
22
23 #include <cerrno>
24 #include <map>
25
26 using std::endl;
27 using std::string;
28
29 namespace lyx {
30
31 using support::package;
32 using support::getEnv;
33 using support::setEnv;
34
35
36 static boost::regex const reg("^([^\\[]*)\\[\\[[^\\]]*\\]\\]$");
37
38
39 #ifdef ENABLE_NLS
40
41
42 #if 0
43
44 -#include <locale>
45
46 // This version of the Pimpl utilizes the message capability of
47 // libstdc++ that is distributed with GNU G++.
48 class Messages::Pimpl {
49 public:
50         typedef std::messages<char>::catalog catalog;
51
52         Pimpl(string const & l)
53                 : lang_(l),
54                   loc_gl(lang_.c_str()),
55                   mssg_gl(std::use_facet<std::messages<char> >(loc_gl))
56         {
57                 //lyxerr << "Messages: language(" << l
58                 //       << ") in dir(" << dir << ")" << endl;
59
60                 cat_gl = mssg_gl.open(PACKAGE, loc_gl, package().locale_dir().c_str());
61
62         }
63
64         ~Pimpl()
65         {
66                 mssg_gl.close(cat_gl);
67         }
68
69         docstring const get(string const & msg) const
70         {
71                 return mssg_gl.get(cat_gl, 0, 0, msg);
72         }
73 private:
74         ///
75         string lang_;
76         ///
77         std::locale loc_gl;
78         ///
79         std::messages<char> const & mssg_gl;
80         ///
81         catalog cat_gl;
82 };
83 #else
84
85 #ifdef HAVE_LOCALE_H
86 #  include <locale.h>
87 #endif
88
89 #  if HAVE_GETTEXT
90 #    include <libintl.h>      // use the header already in the system *EK*
91 #  else
92 #    include "../intl/libintl.h"
93 #  endif
94
95 // This is a more traditional variant.
96 class Messages::Pimpl {
97 public:
98         Pimpl(string const & l)
99                 : lang_(l)
100         {
101                 if ( lang_.empty() ) {
102                         char const * lc_msgs = 0;
103 #ifdef HAVE_LC_MESSAGES
104                         lc_msgs = setlocale(LC_MESSAGES, NULL);
105 #endif
106                         lang_ = lc_msgs ? lc_msgs : "";
107                 }
108                 // strip off any encoding suffix, i.e., assume 8-bit po files
109                 string::size_type i = lang_.find(".");
110                 lang_ = lang_.substr(0, i);
111                 lyxerr[Debug::DEBUG] << BOOST_CURRENT_FUNCTION
112                                      << ": language(" << lang_ << ")" << endl;
113         }
114
115         ~Pimpl() {}
116
117         docstring const & get(string const & m) const
118         {
119                 static docstring empty_string;
120                 if (m.empty())
121                         return empty_string;
122
123                 // Look for the translated string in the cache.
124                 CacheType::iterator it = cache_.find(m);
125                 if (it != cache_.end())
126                         return it->second;
127                 // The string was not found, use gettext to generate it:
128
129                 // In this order, see support/filetools.C:
130                 string lang = getEnv("LC_ALL");
131                 if (lang.empty()) {
132                         lang = getEnv("LC_MESSAGES");
133                         if (lang.empty()) {
134                                 lang = getEnv("LANG");
135                                 if (lang.empty())
136                                         lang = "C";
137                         }
138                 }
139 #ifdef HAVE_LC_MESSAGES
140                 char const * lc_msgs = setlocale(LC_MESSAGES, lang_.c_str());
141 #endif
142                 // setlocale fails (returns NULL) if the corresponding locale
143                 // is not installed.
144                 // On windows (mingw and cygwin) it always returns NULL.
145                 // Since this method gets called for every translatable
146                 // buffer string like e.g. "Figure:" we warn only once.
147 #if !defined(_WIN32) && !defined(__CYGWIN__)
148                 static bool warned = false;
149                 if (!warned && !lc_msgs) {
150                         warned = true;
151                         lyxerr << "Locale " << lang_ << " could not be set" << endl;
152                 }
153 #endif
154                 // CTYPE controls what getmessage thinks what encoding the po file uses
155                 char const * lc_ctype = setlocale(LC_CTYPE, NULL);
156                 string oldCTYPE = lc_ctype ? lc_ctype : "";
157
158                 setlocale(LC_CTYPE, lang_.c_str());
159                 errno = 0;
160                 char const * c = bindtextdomain(PACKAGE, package().locale_dir().c_str());
161                 int e = errno;
162                 if (e) {
163                         lyxerr[Debug::DEBUG]
164                                 << BOOST_CURRENT_FUNCTION << '\n'
165                                 << "Error code: " << errno << '\n'
166                                 << "Lang, mess: " << lang_ << " " << m << '\n'
167                                 << "Directory : " << package().locale_dir() << '\n'
168                                 << "Rtn value : " << c << endl;
169                 }
170 #ifdef WORDS_BIGENDIAN
171                 static const char * codeset = "UCS-4BE";
172 #else
173                 static const char * codeset = "UCS-4LE";
174 #endif
175                 if (!bind_textdomain_codeset(PACKAGE, codeset)) {
176                         lyxerr[Debug::DEBUG]
177                                 << BOOST_CURRENT_FUNCTION << '\n'
178                                 << "Error code: " << errno << '\n'
179                                 << "Codeset   : " << codeset << '\n'
180                                 << endl;
181                 }
182
183                 textdomain(PACKAGE);
184                 char const * tmp = m.c_str();
185                 char const * msg = gettext(tmp);
186                 docstring translated;
187                 if (!msg || msg == tmp) {
188                         if (!msg)
189                                 lyxerr << "Undefined result from gettext" << endl;
190                         //else
191                         //      lyxerr << "Same as entered returned" << endl;
192                         // Some english words have different translations,
193                         // depending on context. In these cases the original
194                         // string is augmented by context information (e.g.
195                         // "To:[[as in 'From page x to page y']]" and
196                         // "To:[[as in 'From format x to format y']]".
197                         // This means that we need to filter out everything
198                         // in double square brackets at the end of the
199                         // string, otherwise the user sees bogus messages.
200                         // If we are unable to honour the request we just
201                         // return what we got in.
202                         boost::smatch sub;
203                         if (regex_match(m, sub, reg))
204                                 translated = from_ascii(sub.str(1));
205                         else
206                                 translated = from_ascii(tmp);
207                 } else {
208                         lyxerr[Debug::DEBUG] << "We got a translation" << endl;
209                         char_type const * ucs4 = reinterpret_cast<char_type const *>(msg);
210                         translated = ucs4;
211                 }
212 #ifdef HAVE_LC_MESSAGES
213                 setlocale(LC_MESSAGES, lang.c_str());
214 #endif
215                 setlocale(LC_CTYPE, oldCTYPE.c_str());
216
217                 it = cache_.insert(std::make_pair(m, translated)).first;
218                 return it->second;
219         }
220 private:
221         ///
222         string lang_;
223         typedef std::map<string, docstring> CacheType;
224         /// Internal cache for gettext translated strings.
225         /// This is needed for performance reason within \c updateLabels()
226         /// under Windows.
227         mutable CacheType cache_;
228 };
229 #endif
230
231 #else // ENABLE_NLS
232 // This is the dummy variant.
233 class Messages::Pimpl {
234 public:
235         Pimpl(string const &) {}
236
237         ~Pimpl() {}
238
239         docstring const get(string const & m) const
240         {
241                 // See comment above
242                 boost::smatch sub;
243                 if (regex_match(m, sub, reg))
244                         return from_ascii(sub.str(1));
245                 else
246                         return from_ascii(m);
247         }
248 };
249 #endif
250
251
252 Messages::Messages()
253         : pimpl_(new Pimpl(""))
254 {}
255
256
257 Messages::Messages(string const & l)
258         : pimpl_(new Pimpl(l))
259 {}
260
261
262 // We need this for the sake of scoped_ptr
263 Messages::~Messages()
264 {}
265
266
267 docstring const & Messages::get(string const & msg) const
268 {
269         return pimpl_->get(msg);
270 }
271
272
273 } // namespace lyx