]> git.lyx.org Git - lyx.git/blob - src/Language.cpp
Align fontenc with document fonts
[lyx.git] / src / Language.cpp
1 /**
2  * \file Language.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author Jean-Marc Lasgouttes
8  * \author Jürgen Spitzmüller
9  * \author Dekel Tsur
10  *
11  * Full author contact details are available in file CREDITS.
12  */
13
14 #include <config.h>
15
16 #include "Language.h"
17
18 #include "Encoding.h"
19 #include "LaTeXFonts.h"
20 #include "Lexer.h"
21 #include "LyXRC.h"
22
23 #include "support/debug.h"
24 #include "support/FileName.h"
25 #include "support/filetools.h"
26 #include "support/lassert.h"
27 #include "support/lstrings.h"
28 #include "support/Messages.h"
29
30 using namespace std;
31 using namespace lyx::support;
32
33 namespace lyx {
34
35 Languages languages;
36 Language const * ignore_language = 0;
37 Language const * default_language = 0;
38 Language const * latex_language = 0;
39 Language const * reset_language = 0;
40
41
42 bool Language::isPolyglossiaExclusive() const
43 {
44         return babel().empty() && !polyglossia().empty() && requires().empty();
45 }
46
47
48 bool Language::isBabelExclusive() const
49 {
50         return !babel().empty() && polyglossia().empty() && requires().empty();
51 }
52
53
54 docstring const Language::translateLayout(string const & m) const
55 {
56         if (m.empty())
57                 return docstring();
58
59         if (!isAscii(m)) {
60                 lyxerr << "Warning: not translating `" << m
61                        << "' because it is not pure ASCII.\n";
62                 return from_utf8(m);
63         }
64
65         TranslationMap::const_iterator it = layoutTranslations_.find(m);
66         if (it != layoutTranslations_.end())
67                 return it->second;
68
69         docstring t = from_ascii(m);
70         cleanTranslation(t);
71         return t;
72 }
73
74
75 vector<string> Language::fontencs() const
76 {
77         return fontenc_;
78 }
79
80
81 string Language::fontenc(BufferParams const & params) const
82 {
83         // Determine optimal font encoding
84         // We check whether the used rm font supports an encoding our language supports
85         for (auto & fe : fontenc_) {
86                 LaTeXFont const & lf = theLaTeXFonts().getLaTeXFont(from_ascii(params.fontsRoman()));
87                 // ASCII means: support all T* encodings plus OT1
88                 if (fe == "ASCII") {
89                         vector<string> const lfe = lf.fontencs();
90                         for (auto & afe : lfe) {
91                                 if (afe == "OT1" || prefixIs(afe, "T"))
92                                         // we found a suitable one; return that.
93                                         return afe;
94                         }
95                 }
96                 // For other encodings, just check whether the font supports it
97                 if (lf.hasFontenc(fe))
98                         return fe;
99         }
100         // We did not find a suitable one; just take the first in the list,
101         // the priorized one (which is "T1" for ASCII).
102         return fontencs().front() == "ASCII" ? "T1" : fontencs().front();
103 }
104
105
106 bool Language::readLanguage(Lexer & lex)
107 {
108         enum LanguageTags {
109                 LA_AS_BABELOPTS = 1,
110                 LA_BABELNAME,
111                 LA_ENCODING,
112                 LA_END,
113                 LA_FONTENC,
114                 LA_GUINAME,
115                 LA_HAS_GUI_SUPPORT,
116                 LA_INTERNAL_ENC,
117                 LA_LANG_CODE,
118                 LA_LANG_VARIETY,
119                 LA_POLYGLOSSIANAME,
120                 LA_POLYGLOSSIAOPTS,
121                 LA_POSTBABELPREAMBLE,
122                 LA_PREBABELPREAMBLE,
123                 LA_PROVIDES,
124                 LA_REQUIRES,
125                 LA_QUOTESTYLE,
126                 LA_RTL
127         };
128
129         // Keep these sorted alphabetically!
130         LexerKeyword languageTags[] = {
131                 { "asbabeloptions",       LA_AS_BABELOPTS },
132                 { "babelname",            LA_BABELNAME },
133                 { "encoding",             LA_ENCODING },
134                 { "end",                  LA_END },
135                 { "fontencoding",         LA_FONTENC },
136                 { "guiname",              LA_GUINAME },
137                 { "hasguisupport",        LA_HAS_GUI_SUPPORT },
138                 { "internalencoding",     LA_INTERNAL_ENC },
139                 { "langcode",             LA_LANG_CODE },
140                 { "langvariety",          LA_LANG_VARIETY },
141                 { "polyglossianame",      LA_POLYGLOSSIANAME },
142                 { "polyglossiaopts",      LA_POLYGLOSSIAOPTS },
143                 { "postbabelpreamble",    LA_POSTBABELPREAMBLE },
144                 { "prebabelpreamble",     LA_PREBABELPREAMBLE },
145                 { "provides",             LA_PROVIDES },
146                 { "quotestyle",           LA_QUOTESTYLE },
147                 { "requires",             LA_REQUIRES },
148                 { "rtl",                  LA_RTL }
149         };
150
151         bool error = false;
152         bool finished = false;
153         lex.pushTable(languageTags);
154         // parse style section
155         while (!finished && lex.isOK() && !error) {
156                 int le = lex.lex();
157                 // See comment in LyXRC.cpp.
158                 switch (le) {
159                 case Lexer::LEX_FEOF:
160                         continue;
161
162                 case Lexer::LEX_UNDEF: // parse error
163                         lex.printError("Unknown language tag `$$Token'");
164                         error = true;
165                         continue;
166
167                 default:
168                         break;
169                 }
170                 switch (static_cast<LanguageTags>(le)) {
171                 case LA_END: // end of structure
172                         finished = true;
173                         break;
174                 case LA_AS_BABELOPTS:
175                         lex >> as_babel_options_;
176                         break;
177                 case LA_BABELNAME:
178                         lex >> babel_;
179                         break;
180                 case LA_POLYGLOSSIANAME:
181                         lex >> polyglossia_name_;
182                         break;
183                 case LA_POLYGLOSSIAOPTS:
184                         lex >> polyglossia_opts_;
185                         break;
186                 case LA_QUOTESTYLE:
187                         lex >> quote_style_;
188                         break;
189                 case LA_ENCODING:
190                         lex >> encodingStr_;
191                         break;
192                 case LA_FONTENC: {
193                         lex.eatLine();
194                         vector<string> const fe =
195                                 getVectorFromString(lex.getString(true), "|");
196                         fontenc_.insert(fontenc_.end(), fe.begin(), fe.end());
197                         break;
198                 }
199                 case LA_GUINAME:
200                         lex >> display_;
201                         break;
202                 case LA_HAS_GUI_SUPPORT:
203                         lex >> has_gui_support_;
204                         break;
205                 case LA_INTERNAL_ENC:
206                         lex >> internal_enc_;
207                         break;
208                 case LA_LANG_CODE:
209                         lex >> code_;
210                         break;
211                 case LA_LANG_VARIETY:
212                         lex >> variety_;
213                         break;
214                 case LA_POSTBABELPREAMBLE:
215                         babel_postsettings_ =
216                                 lex.getLongString(from_ascii("EndPostBabelPreamble"));
217                         break;
218                 case LA_PREBABELPREAMBLE:
219                         babel_presettings_ =
220                                 lex.getLongString(from_ascii("EndPreBabelPreamble"));
221                         break;
222                 case LA_REQUIRES:
223                         lex >> requires_;
224                         break;
225                 case LA_PROVIDES:
226                         lex >> provides_;
227                         break;
228                 case LA_RTL:
229                         lex >> rightToLeft_;
230                         break;
231                 }
232         }
233         lex.popTable();
234         return finished && !error;
235 }
236
237
238 bool Language::read(Lexer & lex)
239 {
240         as_babel_options_ = 0;
241         encoding_ = 0;
242         internal_enc_ = 0;
243         rightToLeft_ = 0;
244
245         if (!lex.next()) {
246                 lex.printError("No name given for language: `$$Token'.");
247                 return false;
248         }
249
250         lang_ = lex.getString();
251         LYXERR(Debug::INFO, "Reading language " << lang_);
252         if (!readLanguage(lex)) {
253                 LYXERR0("Error parsing language `" << lang_ << '\'');
254                 return false;
255         }
256
257         encoding_ = encodings.fromLyXName(encodingStr_);
258         if (!encoding_ && !encodingStr_.empty()) {
259                 encoding_ = encodings.fromLyXName("iso8859-1");
260                 LYXERR0("Unknown encoding " << encodingStr_);
261         }
262         if (fontenc_.empty())
263                 fontenc_.push_back("ASCII");
264         return true;
265 }
266
267
268 void Language::readLayoutTranslations(Language::TranslationMap const & trans, bool replace)
269 {
270         TranslationMap::const_iterator const end = trans.end();
271         for (TranslationMap::const_iterator it = trans.begin(); it != end; ++it) {
272                 if (replace
273                         || layoutTranslations_.find(it->first) == layoutTranslations_.end())
274                         layoutTranslations_[it->first] = it->second;
275         }
276 }
277
278
279 void Languages::read(FileName const & filename)
280 {
281         Lexer lex;
282         lex.setFile(filename);
283         lex.setContext("Languages::read");
284         while (lex.isOK()) {
285                 int le = lex.lex();
286                 switch (le) {
287                 case Lexer::LEX_FEOF:
288                         continue;
289
290                 default:
291                         break;
292                 }
293                 if (lex.getString() != "Language") {
294                         lex.printError("Unknown Language tag `$$Token'");
295                         continue;
296                 }
297                 Language l;
298                 l.read(lex);
299                 if (!lex)
300                         break;
301                 if (l.lang() == "latex") {
302                         // Check if latex language was not already defined.
303                         LASSERT(latex_language == 0, continue);
304                         static const Language latex_lang = l;
305                         latex_language = &latex_lang;
306                 } else if (l.lang() == "ignore") {
307                         // Check if ignore language was not already defined.
308                         LASSERT(ignore_language == 0, continue);
309                         static const Language ignore_lang = l;
310                         ignore_language = &ignore_lang;
311                 } else
312                         languagelist[l.lang()] = l;
313         }
314
315         default_language = getLanguage("english");
316         if (!default_language) {
317                 LYXERR0("Default language \"english\" not found!");
318                 default_language = &(*languagelist.begin()).second;
319                 LYXERR0("Using \"" << default_language->lang() << "\" instead!");
320         }
321
322         // Read layout translations
323         FileName const path = libFileSearch(string(), "layouttranslations");
324         readLayoutTranslations(path);
325 }
326
327
328 namespace {
329
330 bool readTranslations(Lexer & lex, Language::TranslationMap & trans)
331 {
332         while (lex.isOK()) {
333                 if (lex.checkFor("End"))
334                         break;
335                 if (!lex.next(true))
336                         return false;
337                 string const key = lex.getString();
338                 if (!lex.next(true))
339                         return false;
340                 docstring const val = lex.getDocString();
341                 trans[key] = val;
342         }
343         return true;
344 }
345
346
347 enum Match {
348         NoMatch,
349         ApproximateMatch,
350         ExactMatch
351 };
352
353
354 Match match(string const & code, Language const & lang)
355 {
356         // we need to mimic gettext: code can be a two-letter code, which
357         // should match all variants, e.g. "de" should match "de_DE",
358         // "de_AT" etc.
359         // special case for chinese:
360         // simplified  => code == "zh_CN", langcode == "zh_CN"
361         // traditional => code == "zh_TW", langcode == "zh_CN"
362         string const variety = lang.variety();
363         string const langcode = variety.empty() ?
364                                 lang.code() : lang.code() + '_' + variety;
365         string const name = lang.lang();
366         if ((code == langcode && name != "chinese-traditional")
367                 || (code == "zh_TW"  && name == "chinese-traditional"))
368                 return ExactMatch;
369         if ((code.size() == 2) && (langcode.size() > 2)
370                 && (code + '_' == langcode.substr(0, 3)))
371                 return ApproximateMatch;
372         return NoMatch;
373 }
374
375 } // namespace
376
377
378 void Languages::readLayoutTranslations(support::FileName const & filename)
379 {
380         Lexer lex;
381         lex.setFile(filename);
382         lex.setContext("Languages::read");
383
384         // 1) read all translations (exact and approximate matches) into trans
385         typedef std::map<string, Language::TranslationMap> TransMap;
386         TransMap trans;
387         LanguageList::iterator const lbeg = languagelist.begin();
388         LanguageList::iterator const lend = languagelist.end();
389         while (lex.isOK()) {
390                 if (!lex.checkFor("Translation")) {
391                         if (lex.isOK())
392                                 lex.printError("Unknown layout translation tag `$$Token'");
393                         break;
394                 }
395                 if (!lex.next(true))
396                         break;
397                 string const code = lex.getString();
398                 bool found = false;
399                 for (LanguageList::iterator lit = lbeg; lit != lend; ++lit) {
400                         if (match(code, lit->second) != NoMatch) {
401                                 found = true;
402                                 break;
403                         }
404                 }
405                 if (!found) {
406                         lex.printError("Unknown language `" + code + "'");
407                         break;
408                 }
409                 if (!readTranslations(lex, trans[code])) {
410                         lex.printError("Could not read layout translations for language `"
411                                 + code + "'");
412                         break;
413                 }
414         }
415
416         // 2) merge all translations into the languages
417         // exact translations overwrite approximate ones
418         TransMap::const_iterator const tbeg = trans.begin();
419         TransMap::const_iterator const tend = trans.end();
420         for (TransMap::const_iterator tit = tbeg; tit != tend; ++tit) {
421                 for (LanguageList::iterator lit = lbeg; lit != lend; ++lit) {
422                         Match const m = match(tit->first, lit->second);
423                         if (m == NoMatch)
424                                 continue;
425                         lit->second.readLayoutTranslations(tit->second,
426                                                            m == ExactMatch);
427                 }
428         }
429
430 }
431
432
433 Language const * Languages::getLanguage(string const & language) const
434 {
435         if (language == "reset")
436                 return reset_language;
437         if (language == "ignore")
438                 return ignore_language;
439         const_iterator it = languagelist.find(language);
440         return it == languagelist.end() ? reset_language : &it->second;
441 }
442
443
444 } // namespace lyx