// tinygettext - A gettext replacement that works directly on .po files // Copyright (c) 2006 Ingo Ruhnke // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgement in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. #include "precompiled.h" #include "tinygettext/dictionary_manager.hpp" #include #include #include #include #include #include #include "tinygettext/log_stream.hpp" #include "tinygettext/po_parser.hpp" #include "tinygettext/unix_file_system.hpp" namespace tinygettext { static bool has_suffix(const std::string& lhs, const std::string& rhs) { if (lhs.length() < rhs.length()) return false; else return lhs.compare(lhs.length() - rhs.length(), rhs.length(), rhs) == 0; } DictionaryManager::DictionaryManager(const std::string& charset_) : dictionaries(), search_path(), charset(charset_), use_fuzzy(true), current_language(), current_dict(nullptr), empty_dict(), filesystem(new UnixFileSystem) { } DictionaryManager::~DictionaryManager() { for(Dictionaries::iterator i = dictionaries.begin(); i != dictionaries.end(); ++i) { delete i->second; } } void DictionaryManager::clear_cache() { for(Dictionaries::iterator i = dictionaries.begin(); i != dictionaries.end(); ++i) { delete i->second; } dictionaries.clear(); current_dict = nullptr; } Dictionary& DictionaryManager::get_dictionary() { if (current_dict) { return *current_dict; } else { if (current_language) { current_dict = &get_dictionary(current_language); return *current_dict; } else { return empty_dict; } } } Dictionary& DictionaryManager::get_dictionary(const Language& language) { //log_debug << "Dictionary for language \"" << spec << "\" requested" << std::endl; //log_debug << "...normalized as \"" << lang << "\"" << std::endl; assert(language); Dictionaries::iterator i = dictionaries.find(language); if (i != dictionaries.end()) { return *i->second; } else // Dictionary for languages lang isn't loaded, so we load it { //log_debug << "get_dictionary: " << lang << std::endl; Dictionary* dict = new Dictionary(charset); dictionaries[language] = dict; for (SearchPath::reverse_iterator p = search_path.rbegin(); p != search_path.rend(); ++p) { std::vector files = filesystem->open_directory(*p); std::string best_filename; int best_score = 0; for (std::vector::iterator filename = files.begin(); filename != files.end(); ++filename) { // check if filename matches requested language if (has_suffix(*filename, ".po")) { // ignore anything that isn't a .po file Language po_language = Language::from_env(convertFilename2Language(*filename)); if (!po_language) { log_warning << *filename << ": warning: ignoring, unknown language" << std::endl; } else { int score = Language::match(language, po_language); if (score > best_score) { best_score = score; best_filename = *filename; } } } } if (!best_filename.empty()) { std::string pofile = *p + "/" + best_filename; try { std::unique_ptr in = filesystem->open_file(pofile); if (!in) { log_error << "error: failure opening: " << pofile << std::endl; } else { POParser::parse(pofile, *in, *dict); } } catch(std::exception& e) { log_error << "error: failure parsing: " << pofile << std::endl; log_error << e.what() << "" << std::endl; } } } if (!language.get_country().empty()) { // printf("Adding language fallback %s\n", language.get_language().c_str()); dict->addFallback( &get_dictionary(Language::from_spec(language.get_language())) ); } return *dict; } } std::set DictionaryManager::get_languages() { std::set languages; for (SearchPath::iterator p = search_path.begin(); p != search_path.end(); ++p) { std::vector files = filesystem->open_directory(*p); for(std::vector::iterator file = files.begin(); file != files.end(); ++file) { if (has_suffix(*file, ".po")) { languages.insert(Language::from_env(file->substr(0, file->size()-3))); } } } return languages; } void DictionaryManager::set_language(const Language& language) { if (current_language != language) { current_language = language; current_dict = nullptr; } } Language DictionaryManager::get_language() const { return current_language; } void DictionaryManager::set_charset(const std::string& charset_) { clear_cache(); // changing charset invalidates cache charset = charset_; } void DictionaryManager::set_use_fuzzy(bool t) { clear_cache(); use_fuzzy = t; } bool DictionaryManager::get_use_fuzzy() const { return use_fuzzy; } void DictionaryManager::add_directory(const std::string& pathname, bool precedence /* = false */) { clear_cache(); // adding directories invalidates cache if (precedence) search_path.push_front(pathname); else search_path.push_back(pathname); } void DictionaryManager::remove_directory(const std::string& pathname) { SearchPath::iterator it = std::find(search_path.begin(), search_path.end(), pathname); if (it != search_path.end()) { clear_cache(); // removing directories invalidates cache search_path.erase(it); } } void DictionaryManager::set_filesystem(std::unique_ptr filesystem_) { filesystem = std::move(filesystem_); } // ---------------------------------------------------------------------------- /** This function converts a .po filename (e.g. zh_TW.po) into a language * specification (zh_TW). On case insensitive file systems (think windows) * the filename and therefore the country specification is lower case * (zh_tw). It Converts the lower case characters of the country back to * upper case, otherwise tinygettext does not identify the country * correctly. */ std::string DictionaryManager::convertFilename2Language(const std::string &s_in) const { std::string s; if(s_in.substr(s_in.size()-3, 3)==".po") s = s_in.substr(0, s_in.size()-3); else s = s_in; bool underscore_found = false; for(unsigned int i=0; i(::toupper(s[i])); } else underscore_found = s[i]=='_'; } return s; } // convertFilename2Language } // namespace tinygettext /* EOF */