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"
25 # define N_(str) (str) // for marking strings to be translated
31 void cleanTranslation(docstring & trans)
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.
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);
61 # include <libintl.h> // use the header already in the system *EK*
63 # include "intl/libintl.h"
66 using namespace lyx::support;
70 // This version use the traditional gettext.
71 Messages::Messages(string const & l)
72 : lang_(l), warned_(false)
74 // strip off any encoding suffix, i.e., assume 8-bit po files
75 size_t i = lang_.find(".");
76 lang_ = lang_.substr(0, i);
77 LYXERR(Debug::LOCALE, "language(" << lang_ << ")");
84 string const locale_dir = package().locale_dir().toFilesystemEncoding();
85 char const * c = bindtextdomain(PACKAGE, locale_dir.c_str());
88 LYXERR(Debug::LOCALE, "Error code: " << errno << '\n'
89 << "Directory : " << package().locale_dir().absFileName() << '\n'
90 << "Rtn value : " << c);
93 if (!bind_textdomain_codeset(PACKAGE, ucs4_codeset)) {
94 LYXERR(Debug::LOCALE, "Error code: " << errno << '\n'
95 << "Codeset : " << ucs4_codeset);
102 string Messages::language() const
104 // get the language from the gmo file
105 string const test = N_("[[Replace with the code of your language]]");
106 string const trans = to_utf8(get(test));
108 LYXERR0("Something is weird.");
115 bool Messages::available() const
117 return !language().empty();
121 docstring const Messages::get(string const & m) const
126 // Look for the translated string in the cache.
127 TranslationCache::iterator it = cache_.find(m);
128 if (it != cache_.end())
131 // The string was not found, use gettext to generate it
132 static string oldLC_ALL;
133 static string oldLANGUAGE;
134 if (!lang_.empty()) {
135 oldLC_ALL = getEnv("LC_ALL");
136 // This GNU extension overrides any language locale
138 LYXERR(Debug::LOCALE, "Setting LANGUAGE to " << lang_);
139 oldLANGUAGE = getEnv("LANGUAGE");
140 if (!setEnv("LANGUAGE", lang_))
141 LYXERR(Debug::LOCALE, "\t... failed!");
142 // However, setting LANGUAGE does nothing when the
143 // locale is "C". Therefore we set the locale to
144 // something that is believed to exist on most
145 // systems. The idea is that one should be able to
146 // load German documents even without having de_DE
148 LYXERR(Debug::LOCALE, "Setting LC_ALL to en_US");
149 if (!setEnv("LC_ALL", "en_US"))
150 LYXERR(Debug::LOCALE, "\t... failed!");
151 #ifdef HAVE_LC_MESSAGES
152 setlocale(LC_MESSAGES, "");
156 // FIXME: gettext sometimes "forgets" the ucs4_codeset we set
157 // in init(), which leads to severe message corruption (#7371)
158 // We set it again here unconditionally. A real fix must be found!
159 LASSERT(bind_textdomain_codeset(PACKAGE, ucs4_codeset), /**/);
161 char const * m_c = m.c_str();
162 char const * trans_c = gettext(m_c);
165 LYXERR(Debug::LOCALE, "Undefined result from gettext for `" << m << "'.");
166 trans = from_ascii(m);
167 } else if (trans_c == m_c) {
168 //LYXERR(Debug::LOCALE, "Same as entered returned");
169 trans = from_ascii(m);
171 //LYXERR(Debug::LOCALE, "We got a translation");
172 // m is actually not a char const * but ucs4 data
173 trans = reinterpret_cast<char_type const *>(trans_c);
176 cleanTranslation(trans);
178 // Reset environment variables as they were.
179 if (!lang_.empty()) {
180 // Reset everything as it was.
181 LYXERR(Debug::LOCALE, "restoring LANGUAGE from "
182 << getEnv("LANGUAGE")
183 << " to " << oldLANGUAGE);
184 if (!setEnv("LANGUAGE", oldLANGUAGE))
185 LYXERR(Debug::LOCALE, "\t... failed!");
186 LYXERR(Debug::LOCALE, "restoring LC_ALL from " << getEnv("LC_ALL")
187 << " to " << oldLC_ALL);
188 if (!setEnv("LC_ALL", oldLC_ALL))
189 LYXERR(Debug::LOCALE, "\t... failed!");
190 #ifdef HAVE_LC_MESSAGES
191 setlocale(LC_MESSAGES, "");
195 pair<TranslationCache::iterator, bool> result =
196 cache_.insert(make_pair(m, trans));
198 LASSERT(result.second, /**/);
200 return result.first->second;
206 // This is the dummy variant.
210 Messages::Messages(string const & /* l */) {}
212 void Messages::init()
217 docstring const Messages::get(string const & m) const
219 docstring trans = from_ascii(m);
220 cleanTranslation(trans);
225 bool Messages::available() const
240 // This version of the Pimpl utilizes the message capability of
241 // libstdc++ that is distributed with GNU G++.
242 class Messages::Pimpl {
244 typedef messages<char>::catalog catalog;
246 Pimpl(string const & l)
248 loc_gl(lang_.c_str()),
249 mssg_gl(use_facet<messages<char> >(loc_gl))
251 //LYXERR("Messages: language(" << l << ") in dir(" << dir << ")");
253 string const locale_dir = package().locale_dir().toFilesystemEncoding();
254 cat_gl = mssg_gl.open(PACKAGE, loc_gl, locale_dir.c_str());
260 mssg_gl.close(cat_gl);
263 docstring const get(string const & msg) const
265 return mssg_gl.get(cat_gl, 0, 0, msg);
273 messages<char> const & mssg_gl;