+
+ // Read layout translations
+ FileName const path = libFileSearch(string(), "layouttranslations");
+ readLayoutTranslations(path);
+}
+
+
+namespace {
+
+bool readTranslations(Lexer & lex, Language::TranslationMap & trans)
+{
+ while (lex.isOK()) {
+ if (lex.checkFor("End"))
+ break;
+ if (!lex.next(true))
+ return false;
+ string const key = lex.getString();
+ if (!lex.next(true))
+ return false;
+ docstring const val = lex.getDocString();
+ trans[key] = val;
+ }
+ return true;
+}
+
+
+enum Match {
+ NoMatch,
+ ApproximateMatch,
+ VeryApproximateMatch,
+ ExactMatch
+};
+
+
+Match match(string const & code, Language const & lang)
+{
+ // we need to mimic gettext: code can be a two-letter code, which
+ // should match all variants, e.g. "de" should match "de_DE",
+ // "de_AT" etc.
+ // special case for chinese:
+ // simplified => code == "zh_CN", langcode == "zh_CN"
+ // traditional => code == "zh_TW", langcode == "zh_CN"
+ string const variety = lang.variety();
+ string const langcode = variety.empty() ?
+ lang.code() : lang.code() + '_' + variety;
+ string const name = lang.lang();
+ if ((code == langcode && name != "chinese-traditional")
+ || (code == "zh_TW" && name == "chinese-traditional"))
+ return ExactMatch;
+ if ((code.size() == 2) && (langcode.size() > 2)
+ && (code + '_' == langcode.substr(0, 3)))
+ return ApproximateMatch;
+ if (code.substr(0,2) == langcode.substr(0,2))
+ return VeryApproximateMatch;
+ return NoMatch;
+}
+
+} // namespace
+
+
+
+Language const * Languages::getFromCode(string const & code) const
+{
+ // 1/ exact match with any known language
+ for (auto const & l : languagelist_) {
+ if (match(code, l.second) == ExactMatch)
+ return &l.second;
+ }
+
+ // 2/ approximate with any known language
+ for (auto const & l : languagelist_) {
+ if (match(code, l.second) == ApproximateMatch)
+ return &l.second;
+ }
+ return nullptr;
+}
+
+
+Language const * Languages::getFromCode(string const & code,
+ set<Language const *> const & tryfirst) const
+{
+ // 1/ exact match with tryfirst list
+ for (auto const * lptr : tryfirst) {
+ if (match(code, *lptr) == ExactMatch)
+ return lptr;
+ }
+
+ // 2/ approximate match with tryfirst list
+ for (auto const * lptr : tryfirst) {
+ Match const m = match(code, *lptr);
+ if (m == ApproximateMatch || m == VeryApproximateMatch)
+ return lptr;
+ }
+
+ // 3/ stricter match in all languages
+ return getFromCode(code);
+
+ LYXERR0("Unknown language `" << code << "'");
+ return nullptr;
+}
+
+
+void Languages::readLayoutTranslations(support::FileName const & filename)
+{
+ Lexer lex;
+ lex.setFile(filename);
+ lex.setContext("Languages::read");
+
+ // 1) read all translations (exact and approximate matches) into trans
+ std::map<string, Language::TranslationMap> trans;
+ while (lex.isOK()) {
+ if (!lex.checkFor("Translation")) {
+ if (lex.isOK())
+ lex.printError("Unknown layout translation tag `$$Token'");
+ break;
+ }
+ if (!lex.next(true))
+ break;
+ string const code = lex.getString();
+ bool found = getFromCode(code);
+ if (!found) {
+ lex.printError("Unknown language `" + code + "'");
+ break;
+ }
+ if (!readTranslations(lex, trans[code])) {
+ lex.printError("Could not read layout translations for language `"
+ + code + "'");
+ break;
+ }
+ }
+
+ // 2) merge all translations into the languages
+ // exact translations overwrite approximate ones
+ for (auto & tr : trans) {
+ for (auto & lang : languagelist_) {
+ Match const m = match(tr.first, lang.second);
+ if (m == NoMatch)
+ continue;
+ lang.second.readLayoutTranslations(tr.second, m == ExactMatch);
+ }
+ }
+