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)
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);
135 // Trivial wrapper around gettext()
136 docstring const getText(string const & m)
138 // FIXME: gettext sometimes "forgets" the ucs4_codeset we set
139 // in init(), which leads to severe message corruption (#7371)
140 // We set it again here unconditionally. A real fix must be found!
141 LATTEST(bind_textdomain_codeset(PACKAGE, ucs4_codeset));
143 char const * m_c = m.c_str();
144 char const * trans_c = gettext(m_c);
147 LYXERR(Debug::LOCALE, "Undefined result from gettext for `" << m << "'.");
148 trans = from_ascii(m);
149 } else if (trans_c == m_c) {
150 //LYXERR(Debug::LOCALE, "Same as entered returned");
151 trans = from_ascii(m);
153 //LYXERR(Debug::LOCALE, "We got a translation");
154 // m is actually not a char const * but ucs4 data
155 trans = reinterpret_cast<char_type const *>(trans_c);
158 cleanTranslation(trans);
166 docstring const Messages::get(string const & m) const
171 // Look for the translated string in the cache.
172 TranslationCache::iterator it = cache_.find(m);
173 if (it != cache_.end())
176 // The string was not found, use gettext to generate it
178 if (!lang_.empty()) {
179 // This GNU extension overrides any language locale
181 LYXERR(Debug::LOCALE, "Setting LANGUAGE to " << lang_);
182 EnvChanger language_chg("LANGUAGE", lang_);
183 // However, setting LANGUAGE does nothing when the
184 // locale is "C". Therefore we set the locale to
185 // something that is believed to exist on most
186 // systems. The idea is that one should be able to
187 // load German documents even without having de_DE
189 LYXERR(Debug::LOCALE, "Setting LC_ALL to en_US");
190 EnvChanger lc_all_chg("LC_ALL", "en_US");
191 #ifdef HAVE_LC_MESSAGES
192 setlocale(LC_MESSAGES, "");
199 #ifdef HAVE_LC_MESSAGES
200 setlocale(LC_MESSAGES, "");
203 // store translation in cache
204 pair<TranslationCache::iterator, bool> result =
205 cache_.insert(make_pair(m, trans));
207 LASSERT(result.second, return from_utf8(m));
209 return result.first->second;
215 // This is the dummy variant.
219 Messages::Messages(string const & /* l */) {}
221 void Messages::init()
226 docstring const Messages::get(string const & m) const
228 docstring trans = from_ascii(m);
229 cleanTranslation(trans);
233 std::string Messages::language() const
238 bool Messages::available(string const & /* c */)
253 // This version of the Pimpl utilizes the message capability of
254 // libstdc++ that is distributed with GNU G++.
255 class Messages::Pimpl {
257 typedef messages<char>::catalog catalog;
259 Pimpl(string const & l)
261 loc_gl(lang_.c_str()),
262 mssg_gl(use_facet<messages<char> >(loc_gl))
264 //LYXERR("Messages: language(" << l << ") in dir(" << dir << ")");
266 string const locale_dir = package().locale_dir().toFilesystemEncoding();
267 cat_gl = mssg_gl.open(PACKAGE, loc_gl, locale_dir.c_str());
273 mssg_gl.close(cat_gl);
276 docstring const get(string const & msg) const
278 return mssg_gl.get(cat_gl, 0, 0, msg);
286 messages<char> const & mssg_gl;