]> git.lyx.org Git - lyx.git/blob - src/support/Messages.cpp
InsetArgument: Set ResetsFontEdit to false
[lyx.git] / src / support / Messages.cpp
1 /* \file Messages.cpp
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 "support/Messages.h"
13
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"
20
21 #include "support/lassert.h"
22
23 #include <cerrno>
24
25 #  define N_(str) (str)              // for marking strings to be translated
26
27 using namespace std;
28
29 namespace lyx {
30
31 void cleanTranslation(docstring & trans) 
32 {
33         /*
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']]". Also, 
38           when placeholders are used, the context can indicate what will
39           be substituted for the placeholder (e.g. "%1$s[[date]], %1$s
40           [[time]]). This means that we need to filter out everything 
41           in double square brackets at the end of the string, otherwise
42           the user sees bogus messages. If we are unable to honour the
43           request we just return what we got in.
44         */
45         static docstring const ctx_start = from_ascii("[[");
46         static docstring const ctx_end = from_ascii("]]");
47         while (true) {
48                 size_t const pos1 = trans.find(ctx_start);
49                 if (pos1 != docstring::npos) {
50                         size_t const pos2 = trans.find(ctx_end, pos1);
51                         if (pos2 != docstring::npos) {
52                                 trans.erase(pos1, pos2 - pos1 + 2);
53                                 continue;
54                         }
55                 }
56                 break;
57         }
58 }
59
60 } // lyx
61
62
63 #ifdef ENABLE_NLS
64
65 #  ifdef HAVE_LOCALE_H
66 #    include <locale.h>
67 #  endif
68
69 #  if HAVE_GETTEXT
70 #    include <libintl.h>      // use the header already in the system *EK*
71 #  else
72 #    include "intl/libintl.h"
73 #  endif
74
75 using namespace lyx::support;
76
77 namespace lyx {
78
79 // This version use the traditional gettext.
80 Messages::Messages(string const & l)
81         : lang_(l)
82 {
83         // strip off any encoding suffix, i.e., assume 8-bit po files
84         size_t i = lang_.find(".");
85         lang_ = lang_.substr(0, i);
86         LYXERR(Debug::LOCALE, "language(" << lang_ << ")");
87 }
88
89
90 void Messages::init()
91 {
92         errno = 0;
93         string const locale_dir = package().locale_dir().toFilesystemEncoding();
94         char const * c = bindtextdomain(PACKAGE, locale_dir.c_str());
95         int e = errno;
96         if (e) {
97                 LYXERR(Debug::LOCALE, "Error code: " << errno << '\n'
98                         << "Directory : " << package().locale_dir().absFileName() << '\n'
99                         << "Rtn value : " << c);
100         }
101
102         if (!bind_textdomain_codeset(PACKAGE, ucs4_codeset)) {
103                 LYXERR(Debug::LOCALE, "Error code: " << errno << '\n'
104                         << "Codeset   : " << ucs4_codeset);
105         }
106
107         textdomain(PACKAGE);
108 }
109
110
111 string Messages::language() const
112 {
113         // get the language from the gmo file
114         string const test = N_("[[Replace with the code of your language]]");
115         string const trans = to_utf8(get(test));
116         if (trans == test) {
117                 LYXERR0("Something is weird.");
118                 return string();
119         } else
120                 return trans;
121 }
122
123
124 bool Messages::available(string const & c)
125 {
126         static string locale_dir = package().locale_dir().toFilesystemEncoding();
127         string code = c;
128         // this loops at most twice
129         while (true) {
130                 string const filen = locale_dir + "/" + code 
131                         + "/LC_MESSAGES/" PACKAGE ".mo";
132                 if (FileName(filen).isReadableFile())
133                         return true;
134                 if (contains(code, '_'))
135                         code = token(code, '_', 0);
136                 else return false;
137         }
138         return false;
139
140 }
141
142 namespace {
143
144 // Trivial wrapper around gettext()
145 docstring const getText(string const & m)
146 {
147         // FIXME: gettext sometimes "forgets" the ucs4_codeset we set
148         // in init(), which leads to severe message corruption (#7371)
149         // We set it again here unconditionally. A real fix must be found!
150         LATTEST(bind_textdomain_codeset(PACKAGE, ucs4_codeset));
151
152         char const * m_c = m.c_str();
153         char const * trans_c = gettext(m_c);
154         docstring trans;
155         if (!trans_c) {
156                 LYXERR(Debug::LOCALE, "Undefined result from gettext for `" << m << "'.");
157                 trans = from_ascii(m);
158         } else if (trans_c == m_c) {
159                 //LYXERR(Debug::LOCALE, "Same as entered returned");
160                 trans = from_ascii(m);
161         } else {
162                 //LYXERR(Debug::LOCALE, "We got a translation");
163                 // m is actually not a char const * but ucs4 data
164                 trans = reinterpret_cast<char_type const *>(trans_c);
165         }
166
167         cleanTranslation(trans);
168
169         return trans;
170 }
171
172 }
173
174
175 docstring const Messages::get(string const & m) const
176 {
177         if (m.empty())
178                 return docstring();
179
180         // Look for the translated string in the cache.
181         TranslationCache::iterator it = cache_.find(m);
182         if (it != cache_.end())
183                 return it->second;
184
185         // The string was not found, use gettext to generate it
186         docstring trans;
187         if (!lang_.empty()) {
188                 // This GNU extension overrides any language locale
189                 // wrt gettext.
190                 LYXERR(Debug::LOCALE, "Setting LANGUAGE to " << lang_);
191                 EnvChanger language_chg("LANGUAGE", lang_);
192                 // However, setting LANGUAGE does nothing when the
193                 // locale is "C". Therefore we set the locale to
194                 // something that is believed to exist on most
195                 // systems. The idea is that one should be able to
196                 // load German documents even without having de_DE
197                 // installed.
198                 LYXERR(Debug::LOCALE, "Setting LC_ALL to en_US");
199                 EnvChanger lc_all_chg("LC_ALL", "en_US");
200 #ifdef HAVE_LC_MESSAGES
201                 setlocale(LC_MESSAGES, "");
202 #endif
203                 trans = getText(m);
204         } else
205                 trans = getText(m);
206                 
207
208 #ifdef HAVE_LC_MESSAGES
209         setlocale(LC_MESSAGES, "");
210 #endif
211
212         // store translation in cache
213         pair<TranslationCache::iterator, bool> result =
214                 cache_.insert(make_pair(m, trans));
215
216         LASSERT(result.second, return from_utf8(m));
217
218         return result.first->second;
219 }
220
221 } // namespace lyx
222
223 #else // ENABLE_NLS
224 // This is the dummy variant.
225
226 namespace lyx {
227
228 Messages::Messages(string const & /* l */) {}
229
230 void Messages::init()
231 {
232 }
233
234
235 docstring const Messages::get(string const & m) const
236 {
237         docstring trans = from_ascii(m);
238         cleanTranslation(trans);
239         return trans;
240 }
241
242 std::string Messages::language() const
243     {
244         return string();
245     }
246
247 bool Messages::available(string const & /* c */)
248 {
249         return false;
250 }
251
252 } // namespace lyx
253
254 #endif