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(string const & c)
117 static string locale_dir = package().locale_dir().toFilesystemEncoding();
119 // this loops at most twice
121 string const filen = locale_dir + "/" + code
122 + "/LC_MESSAGES/" PACKAGE ".mo";
123 if (FileName(filen).isReadableFile())
125 if (contains(code, '_'))
126 code = token(code, '_', 0);
134 docstring const Messages::get(string const & m) const
139 // Look for the translated string in the cache.
140 TranslationCache::iterator it = cache_.find(m);
141 if (it != cache_.end())
144 // The string was not found, use gettext to generate it
145 static string oldLC_ALL;
146 static string oldLANGUAGE;
147 if (!lang_.empty()) {
148 oldLC_ALL = getEnv("LC_ALL");
149 // This GNU extension overrides any language locale
151 LYXERR(Debug::LOCALE, "Setting LANGUAGE to " << lang_);
152 oldLANGUAGE = getEnv("LANGUAGE");
153 if (!setEnv("LANGUAGE", lang_))
154 LYXERR(Debug::LOCALE, "\t... failed!");
155 // However, setting LANGUAGE does nothing when the
156 // locale is "C". Therefore we set the locale to
157 // something that is believed to exist on most
158 // systems. The idea is that one should be able to
159 // load German documents even without having de_DE
161 LYXERR(Debug::LOCALE, "Setting LC_ALL to en_US");
162 if (!setEnv("LC_ALL", "en_US"))
163 LYXERR(Debug::LOCALE, "\t... failed!");
164 #ifdef HAVE_LC_MESSAGES
165 setlocale(LC_MESSAGES, "");
169 // FIXME: gettext sometimes "forgets" the ucs4_codeset we set
170 // in init(), which leads to severe message corruption (#7371)
171 // We set it again here unconditionally. A real fix must be found!
172 LASSERT(bind_textdomain_codeset(PACKAGE, ucs4_codeset), /**/);
174 char const * m_c = m.c_str();
175 char const * trans_c = gettext(m_c);
178 LYXERR(Debug::LOCALE, "Undefined result from gettext for `" << m << "'.");
179 trans = from_ascii(m);
180 } else if (trans_c == m_c) {
181 //LYXERR(Debug::LOCALE, "Same as entered returned");
182 trans = from_ascii(m);
184 //LYXERR(Debug::LOCALE, "We got a translation");
185 // m is actually not a char const * but ucs4 data
186 trans = reinterpret_cast<char_type const *>(trans_c);
189 cleanTranslation(trans);
191 // Reset environment variables as they were.
192 if (!lang_.empty()) {
193 // Reset everything as it was.
194 LYXERR(Debug::LOCALE, "restoring LANGUAGE from "
195 << getEnv("LANGUAGE")
196 << " to " << oldLANGUAGE);
197 if (!setEnv("LANGUAGE", oldLANGUAGE))
198 LYXERR(Debug::LOCALE, "\t... failed!");
199 LYXERR(Debug::LOCALE, "restoring LC_ALL from " << getEnv("LC_ALL")
200 << " to " << oldLC_ALL);
201 if (!setEnv("LC_ALL", oldLC_ALL))
202 LYXERR(Debug::LOCALE, "\t... failed!");
203 #ifdef HAVE_LC_MESSAGES
204 setlocale(LC_MESSAGES, "");
208 pair<TranslationCache::iterator, bool> result =
209 cache_.insert(make_pair(m, trans));
211 LASSERT(result.second, /**/);
213 return result.first->second;
219 // This is the dummy variant.
223 Messages::Messages(string const & /* l */) {}
225 void Messages::init()
230 docstring const Messages::get(string const & m) const
232 docstring trans = from_ascii(m);
233 cleanTranslation(trans);
237 std::string Messages::language() const
242 bool Messages::available(string const & /* c */)
257 // This version of the Pimpl utilizes the message capability of
258 // libstdc++ that is distributed with GNU G++.
259 class Messages::Pimpl {
261 typedef messages<char>::catalog catalog;
263 Pimpl(string const & l)
265 loc_gl(lang_.c_str()),
266 mssg_gl(use_facet<messages<char> >(loc_gl))
268 //LYXERR("Messages: language(" << l << ") in dir(" << dir << ")");
270 string const locale_dir = package().locale_dir().toFilesystemEncoding();
271 cat_gl = mssg_gl.open(PACKAGE, loc_gl, locale_dir.c_str());
277 mssg_gl.close(cat_gl);
280 docstring const get(string const & msg) const
282 return mssg_gl.get(cat_gl, 0, 0, msg);
290 messages<char> const & mssg_gl;