2 * This file is part of LyX, the document processor.
3 * Licence details can be found in the file COPYING.
5 * \author Lars Gullik Bjønnes
7 * Full author contact details are available in file CREDITS.
12 #include "support/Messages.h"
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"
21 #include "support/lassert.h"
29 // Instanciate static member.
30 string Messages::main_lang_;
34 void cleanTranslation(docstring & trans)
37 Some english words have different translations, depending on
38 context. In these cases the original string is augmented by
39 context information (e.g. "To:[[as in 'From page x to page
40 y']]" and "To:[[as in 'From format x to format y']]". This
41 means that we need to filter out everything in double square
42 brackets at the end of the string, otherwise the user sees
43 bogus messages. If we are unable to honour the request we
44 just return what we got in.
46 size_t const pos1 = trans.find(from_ascii("[["));
47 if (pos1 != docstring::npos) {
48 size_t const pos2 = trans.find(from_ascii("]]"), pos1);
49 if (pos2 != docstring::npos)
50 trans.erase(pos1, pos2 - pos1 + 2);
65 # include <libintl.h> // use the header already in the system *EK*
67 # include "../../intl/libintl.h"
70 using namespace lyx::support;
74 void Messages::setDefaultLanguage()
76 char const * env_lang[5] = {"LANGUAGE", "LC_ALL", "LC_MESSAGES",
77 "LC_MESSAGE", "LANG"};
78 for (size_t i = 0; i != 5; ++i) {
79 string const lang = getEnv(env_lang[i]);
82 Messages::main_lang_ = lang;
86 LYXERR(Debug::LOCALE, "Default language not found!");
90 // This version use the traditional gettext.
91 Messages::Messages(string const & l)
92 : lang_(l), warned_(false)
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_ << ")");
101 void Messages::init()
104 string const locale_dir = package().locale_dir().toFilesystemEncoding();
105 char const * c = bindtextdomain(PACKAGE, locale_dir.c_str());
108 LYXERR(Debug::LOCALE, "Error code: " << errno << '\n'
109 << "Directory : " << package().locale_dir().absFileName() << '\n'
110 << "Rtn value : " << c);
113 if (!bind_textdomain_codeset(PACKAGE, ucs4_codeset)) {
114 LYXERR(Debug::LOCALE, "Error code: " << errno << '\n'
115 << "Codeset : " << ucs4_codeset);
120 // Reset default language;
121 setDefaultLanguage();
125 bool Messages::available() const
127 string const test = languageTestString();
128 string const trans = to_utf8(get(test));
129 return !trans.empty() && trans != test;
133 docstring const Messages::get(string const & m) const
138 // Look for the translated string in the cache.
139 TranslationCache::iterator it = cache_.find(m);
140 if (it != cache_.end())
143 // The string was not found, use gettext to generate it
144 static string oldLC_ALL;
145 static string oldLANGUAGE;
146 if (!lang_.empty()) {
147 oldLC_ALL = getEnv("LC_ALL");
148 // This GNU extension overrides any language locale
150 LYXERR(Debug::LOCALE, "Setting LANGUAGE to " << lang_);
151 oldLANGUAGE = getEnv("LANGUAGE");
152 if (!setEnv("LANGUAGE", lang_))
153 LYXERR(Debug::LOCALE, "\t... failed!");
154 // However, setting LANGUAGE does nothing when the
155 // locale is "C". Therefore we set the locale to
156 // something that is believed to exist on most
157 // systems. The idea is that one should be able to
158 // load German documents even without having de_DE
160 LYXERR(Debug::LOCALE, "Setting LC_ALL to en_US");
161 if (!setEnv("LC_ALL", "en_US"))
162 LYXERR(Debug::LOCALE, "\t... failed!");
163 #ifdef HAVE_LC_MESSAGES
164 setlocale(LC_MESSAGES, "");
168 char const * m_c = m.c_str();
169 char const * trans_c = gettext(m_c);
172 LYXERR(Debug::LOCALE, "Undefined result from gettext for `" << m << "'.");
173 trans = from_ascii(m);
174 } else if (trans_c == m_c) {
175 //LYXERR(Debug::LOCALE, "Same as entered returned");
176 trans = from_ascii(m);
178 //LYXERR(Debug::LOCALE, "We got a translation");
179 // m is actually not a char const * but ucs4 data
180 trans = reinterpret_cast<char_type const *>(trans_c);
183 cleanTranslation(trans);
185 // Reset environment variables as they were.
186 if (!lang_.empty()) {
187 // Reset everything as it was.
188 LYXERR(Debug::LOCALE, "restoring LANGUAGE from "
189 << getEnv("LANGUAGE")
190 << " to " << oldLANGUAGE);
191 if (!setEnv("LANGUAGE", oldLANGUAGE))
192 LYXERR(Debug::LOCALE, "\t... failed!");
193 LYXERR(Debug::LOCALE, "restoring LC_ALL from " << getEnv("LC_ALL")
194 << " to " << oldLC_ALL);
195 if (!setEnv("LC_ALL", oldLC_ALL))
196 LYXERR(Debug::LOCALE, "\t... failed!");
197 #ifdef HAVE_LC_MESSAGES
198 setlocale(LC_MESSAGES, "");
202 pair<TranslationCache::iterator, bool> result =
203 cache_.insert(make_pair(m, trans));
205 LASSERT(result.second, /**/);
207 return result.first->second;
213 // This is the dummy variant.
217 Messages::Messages(string const & /* l */) {}
219 void Messages::init()
224 docstring const Messages::get(string const & m) const
226 docstring trans = from_ascii(m);
227 cleanTranslation(trans);
232 bool Messages::available() const
247 // This version of the Pimpl utilizes the message capability of
248 // libstdc++ that is distributed with GNU G++.
249 class Messages::Pimpl {
251 typedef messages<char>::catalog catalog;
253 Pimpl(string const & l)
255 loc_gl(lang_.c_str()),
256 mssg_gl(use_facet<messages<char> >(loc_gl))
258 //LYXERR("Messages: language(" << l << ") in dir(" << dir << ")");
260 string const locale_dir = package().locale_dir().toFilesystemEncoding();
261 cat_gl = mssg_gl.open(PACKAGE, loc_gl, locale_dir.c_str());
267 mssg_gl.close(cat_gl);
270 docstring const get(string const & msg) const
272 return mssg_gl.get(cat_gl, 0, 0, msg);
280 messages<char> const & mssg_gl;