diff options
Diffstat (limited to 'shared/localization.cpp')
-rw-r--r-- | shared/localization.cpp | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/shared/localization.cpp b/shared/localization.cpp new file mode 100644 index 00000000..825773d9 --- /dev/null +++ b/shared/localization.cpp @@ -0,0 +1,431 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * +// ************************************************************************** +// +#include "localization.h" +#include <fstream> +#include <map> +#include <list> +#include <iterator> +#include <wx/ffile.h> +#include <wx/intl.h> +#include <wx/msgdlg.h> +#include "system_constants.h" +#include "parse_plural.h" +#include "parse_lng.h" +#include "util.h" +#include "string_tools.h" +#include "file_traverser.h" +#include "../shared/standard_paths.h" +#include "../shared/string_conv.h" +#include "i18n.h" + +using namespace zen; + + +namespace +{ +//global objects +wxString THOUSANDS_SEPARATOR = wxT(","); + + +class FFSLocale : public TranslationHandler +{ +public: + FFSLocale(const wxString& filename, wxLanguage languageId); //throw (lngfile::ParsingError, PluralForm::ParsingError) + + wxLanguage langId() const { return langId_; } + + virtual wxString thousandsSeparator() { return THOUSANDS_SEPARATOR; }; + + virtual wxString translate(const wxString& text) + { + //look for translation in buffer table + const Translation::const_iterator iter = transMapping.find(text); + if (iter != transMapping.end()) + return iter->second; + + return text; //fallback + } + + virtual wxString translate(const wxString& singular, const wxString& plural, int n) + { + TranslationPlural::const_iterator iter = transMappingPl.find(std::make_pair(singular, plural)); + if (iter != transMappingPl.end()) + { + const int formNo = pluralParser->getForm(n); + if (0 <= formNo && formNo < static_cast<int>(iter->second.size())) + return iter->second[formNo]; + } + + return n == 1 ? singular : plural; //fallback + } + +private: + typedef std::map<wxString, wxString> Translation; + typedef std::map<std::pair<wxString, wxString>, std::vector<wxString> > TranslationPlural; + + Translation transMapping; //map original text |-> translation + TranslationPlural transMappingPl; + std::auto_ptr<PluralForm> pluralParser; + wxLanguage langId_; +}; + + + +std::string getFileStream(const wxString& filename) //return empty string on error throw() +{ + std::string inputStream; + + //workaround to get a FILE* from a unicode filename in a portable way + wxFFile langFile(filename, wxT("rb")); + if (langFile.IsOpened()) + { + FILE* fpInput = langFile.fp(); + + std::vector<char> buffer(50 * 1024); + size_t bytesRead = 0; + do + { + bytesRead = ::fread(&buffer[0], 1, buffer.size(), fpInput); + inputStream.append(&buffer[0], bytesRead); + } + while (bytesRead == buffer.size()); + } + return inputStream; +} + + +FFSLocale::FFSLocale(const wxString& filename, wxLanguage languageId) : langId_(languageId) //throw (lngfile::ParsingError, PluralForm::ParsingError) +{ + static class StaticInit + { + public: + StaticInit() : loc(wxLANGUAGE_DEFAULT) //wxLocale: we need deferred initialization, sigh... + { + //::setlocale (LC_ALL, ""); -> implicitly called by wxLocale + const lconv* localInfo = ::localeconv(); + + //actually these two parameters are language dependent, but we take system setting to handle all kinds of language derivations + THOUSANDS_SEPARATOR = wxString::FromUTF8(localInfo->thousands_sep); + + // why not working? + // THOUSANDS_SEPARATOR = std::use_facet<std::numpunct<wchar_t> >(std::locale("")).thousands_sep(); + // DECIMAL_POINT = std::use_facet<std::numpunct<wchar_t> >(std::locale("")).decimal_point(); + } + private: + wxLocale loc; //required for RTL language support (and nothing else) + } dummy; + + + const std::string inputStream = getFileStream(filename); + if (inputStream.empty()) + throw lngfile::ParsingError(0, 0); + + lngfile::TransHeader header; + lngfile::TranslationMap transInput; + lngfile::TranslationPluralMap transPluralInput; + lngfile::parseLng(inputStream, header, transInput, transPluralInput); //throw ParsingError + + for (lngfile::TranslationMap::const_iterator i = transInput.begin(); i != transInput.end(); ++i) + { + const wxString original = wxString::FromUTF8(i->first.c_str()); + const wxString translation = wxString::FromUTF8(i->second.c_str()); + assert(!translation.empty()); + transMapping.insert(std::make_pair(original , translation)); + } + + for (lngfile::TranslationPluralMap::const_iterator i = transPluralInput.begin(); i != transPluralInput.end(); ++i) + { + const wxString singular = wxString::FromUTF8(i->first.first.c_str()); + const wxString plural = wxString::FromUTF8(i->first.second.c_str()); + const lngfile::PluralForms& plForms = i->second; + + std::vector<wxString> plFormsWide; + for (lngfile::PluralForms::const_iterator j = plForms.begin(); j != plForms.end(); ++j) + plFormsWide.push_back(wxString::FromUTF8(j->c_str())); + + assert(!plFormsWide.empty()); + + transMappingPl.insert(std::make_pair(std::make_pair(singular, plural), plFormsWide)); + } + + pluralParser.reset(new PluralForm(header.pluralDefinition.c_str())); //throw PluralForm::ParsingError +} +} + + +class FindLngfiles : public zen::TraverseCallback +{ +public: + FindLngfiles(std::vector<Zstring>& lngFiles) : lngFiles_(lngFiles) {} + + virtual void onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details) + { + if (Zstring(shortName).EndsWith(Zstr(".lng"))) + lngFiles_.push_back(fullName); + } + + virtual void onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) {} + virtual ReturnValDir onDir(const Zchar* shortName, const Zstring& fullName) { return Loki::Int2Type<ReturnValDir::TRAVERSING_DIR_IGNORE>(); } + virtual void onError(const wxString& errorText) {} //errors are not really critical in this context + +private: + std::vector<Zstring>& lngFiles_; +}; + + +struct LessTranslation : public std::binary_function<ExistingTranslations::Entry, ExistingTranslations::Entry, bool> +{ + bool operator()(const ExistingTranslations::Entry& lhs, const ExistingTranslations::Entry& rhs) const + { +#ifdef FFS_WIN + //use a more "natural" sort, that is ignore case and diacritics + const int rv = ::CompareString(LOCALE_USER_DEFAULT, //__in LCID Locale, + NORM_IGNORECASE, //__in DWORD dwCmpFlags, + lhs.languageName.c_str(), //__in LPCTSTR lpString1, + static_cast<int>(lhs.languageName.size()), //__in int cchCount1, + rhs.languageName.c_str(), //__in LPCTSTR lpString2, + static_cast<int>(rhs.languageName.size())); //__in int cchCount2 + if (rv == 0) + throw std::runtime_error("Error comparing strings!"); + else + return rv == CSTR_LESS_THAN; //convert to C-style string compare result +#else + return lhs.languageName < rhs.languageName; +#endif + } +}; + + +ExistingTranslations::ExistingTranslations() +{ + { + //default entry: + ExistingTranslations::Entry newEntry; + newEntry.languageID = wxLANGUAGE_ENGLISH_US; + newEntry.languageName = wxT("English (US)"); + newEntry.languageFile = wxT(""); + newEntry.translatorName = wxT("ZenJu"); + newEntry.languageFlag = wxT("usa.png"); + locMapping.push_back(newEntry); + } + + //search language files available + std::vector<Zstring> lngFiles; + FindLngfiles traverseCallback(lngFiles); + + traverseFolder(wxToZ(zen::getResourceDir() + wxT("Languages")), //throw(); + false, //don't follow symlinks + traverseCallback); + + for (std::vector<Zstring>::const_iterator i = lngFiles.begin(); i != lngFiles.end(); ++i) + { + const std::string stream = getFileStream(zToWx(*i)); + if (!stream.empty()) + try + { + lngfile::TransHeader lngHeader; + lngfile::parseHeader(stream, lngHeader); //throw ParsingError + + const wxLanguageInfo* locInfo = wxLocale::FindLanguageInfo(wxString::FromUTF8(lngHeader.localeName.c_str())); + if (locInfo) + { + ExistingTranslations::Entry newEntry; + newEntry.languageID = locInfo->Language; + newEntry.languageName = wxString::FromUTF8(lngHeader.languageName.c_str()); + newEntry.languageFile = zToWx(*i); + newEntry.translatorName = wxString::FromUTF8(lngHeader.translatorName.c_str()); + newEntry.languageFlag = wxString::FromUTF8(lngHeader.flagFile.c_str()); + locMapping.push_back(newEntry); + } + } + catch (lngfile::ParsingError&) {} + } + + std::sort(locMapping.begin(), locMapping.end(), LessTranslation()); +} + + +namespace +{ +wxLanguage mapLanguageDialect(wxLanguage language) +{ + switch (static_cast<int>(language)) //map language dialects + { + //variants of wxLANGUAGE_GERMAN + case wxLANGUAGE_GERMAN_AUSTRIAN: + case wxLANGUAGE_GERMAN_BELGIUM: + case wxLANGUAGE_GERMAN_LIECHTENSTEIN: + case wxLANGUAGE_GERMAN_LUXEMBOURG: + case wxLANGUAGE_GERMAN_SWISS: + return wxLANGUAGE_GERMAN; + + //variants of wxLANGUAGE_FRENCH + case wxLANGUAGE_FRENCH_BELGIAN: + case wxLANGUAGE_FRENCH_CANADIAN: + case wxLANGUAGE_FRENCH_LUXEMBOURG: + case wxLANGUAGE_FRENCH_MONACO: + case wxLANGUAGE_FRENCH_SWISS: + return wxLANGUAGE_FRENCH; + + //variants of wxLANGUAGE_DUTCH + case wxLANGUAGE_DUTCH_BELGIAN: + return wxLANGUAGE_DUTCH; + + //variants of wxLANGUAGE_ITALIAN + case wxLANGUAGE_ITALIAN_SWISS: + return wxLANGUAGE_ITALIAN; + + //variants of wxLANGUAGE_CHINESE_SIMPLIFIED + case wxLANGUAGE_CHINESE: + case wxLANGUAGE_CHINESE_SINGAPORE: + return wxLANGUAGE_CHINESE_SIMPLIFIED; + + //variants of wxLANGUAGE_CHINESE_TRADITIONAL + case wxLANGUAGE_CHINESE_TAIWAN: + case wxLANGUAGE_CHINESE_HONGKONG: + case wxLANGUAGE_CHINESE_MACAU: + return wxLANGUAGE_CHINESE_TRADITIONAL; + + //variants of wxLANGUAGE_RUSSIAN + case wxLANGUAGE_RUSSIAN_UKRAINE: + return wxLANGUAGE_RUSSIAN; + + //variants of wxLANGUAGE_SPANISH + case wxLANGUAGE_SPANISH_ARGENTINA: + case wxLANGUAGE_SPANISH_BOLIVIA: + case wxLANGUAGE_SPANISH_CHILE: + case wxLANGUAGE_SPANISH_COLOMBIA: + case wxLANGUAGE_SPANISH_COSTA_RICA: + case wxLANGUAGE_SPANISH_DOMINICAN_REPUBLIC: + case wxLANGUAGE_SPANISH_ECUADOR: + case wxLANGUAGE_SPANISH_EL_SALVADOR: + case wxLANGUAGE_SPANISH_GUATEMALA: + case wxLANGUAGE_SPANISH_HONDURAS: + case wxLANGUAGE_SPANISH_MEXICAN: + case wxLANGUAGE_SPANISH_MODERN: + case wxLANGUAGE_SPANISH_NICARAGUA: + case wxLANGUAGE_SPANISH_PANAMA: + case wxLANGUAGE_SPANISH_PARAGUAY: + case wxLANGUAGE_SPANISH_PERU: + case wxLANGUAGE_SPANISH_PUERTO_RICO: + case wxLANGUAGE_SPANISH_URUGUAY: + case wxLANGUAGE_SPANISH_US: + case wxLANGUAGE_SPANISH_VENEZUELA: + return wxLANGUAGE_SPANISH; + + //variants of wxLANGUAGE_SWEDISH + case wxLANGUAGE_SWEDISH_FINLAND: + return wxLANGUAGE_SWEDISH; + + //case wxLANGUAGE_CZECH: + //case wxLANGUAGE_FINNISH: + //case wxLANGUAGE_GREEK: + //case wxLANGUAGE_JAPANESE: + //case wxLANGUAGE_POLISH: + //case wxLANGUAGE_SLOVENIAN: + //case wxLANGUAGE_HUNGARIAN: + //case wxLANGUAGE_PORTUGUESE: + //case wxLANGUAGE_PORTUGUESE_BRAZILIAN: + //case wxLANGUAGE_KOREAN: + //case wxLANGUAGE_UKRAINIAN: + + //variants of wxLANGUAGE_ARABIC + case wxLANGUAGE_ARABIC_ALGERIA: + case wxLANGUAGE_ARABIC_BAHRAIN: + case wxLANGUAGE_ARABIC_EGYPT: + case wxLANGUAGE_ARABIC_IRAQ: + case wxLANGUAGE_ARABIC_JORDAN: + case wxLANGUAGE_ARABIC_KUWAIT: + case wxLANGUAGE_ARABIC_LEBANON: + case wxLANGUAGE_ARABIC_LIBYA: + case wxLANGUAGE_ARABIC_MOROCCO: + case wxLANGUAGE_ARABIC_OMAN: + case wxLANGUAGE_ARABIC_QATAR: + case wxLANGUAGE_ARABIC_SAUDI_ARABIA: + case wxLANGUAGE_ARABIC_SUDAN: + case wxLANGUAGE_ARABIC_SYRIA: + case wxLANGUAGE_ARABIC_TUNISIA: + case wxLANGUAGE_ARABIC_UAE: + case wxLANGUAGE_ARABIC_YEMEN: + return wxLANGUAGE_ARABIC; + + //variants of wxLANGUAGE_ENGLISH_UK + case wxLANGUAGE_ENGLISH_AUSTRALIA: + case wxLANGUAGE_ENGLISH_NEW_ZEALAND: + case wxLANGUAGE_ENGLISH_TRINIDAD: + case wxLANGUAGE_ENGLISH_CARIBBEAN: + case wxLANGUAGE_ENGLISH_JAMAICA: + case wxLANGUAGE_ENGLISH_BELIZE: + case wxLANGUAGE_ENGLISH_EIRE: + case wxLANGUAGE_ENGLISH_SOUTH_AFRICA: + case wxLANGUAGE_ENGLISH_ZIMBABWE: + case wxLANGUAGE_ENGLISH_BOTSWANA: + case wxLANGUAGE_ENGLISH_DENMARK: + return wxLANGUAGE_ENGLISH_UK; + + default: + return language; + } +} +} + + +void zen::setLanguage(int language) +{ + //(try to) retrieve language file + wxString languageFile; + for (std::vector<ExistingTranslations::Entry>::const_iterator i = ExistingTranslations::get().begin(); i != ExistingTranslations::get().end(); ++i) + if (i->languageID == language) + { + languageFile = i->languageFile; + break; + } + + + //reset to english language; in case of error show error message just once + zen::setTranslator(); + + //load language file into buffer + if (!languageFile.empty()) //if languageFile is empty texts will be english per default + { + try + { + zen::setTranslator(new FFSLocale(languageFile, static_cast<wxLanguage>(language))); //throw (lngfile::ParsingError, PluralForm::ParsingError) + } + catch (lngfile::ParsingError& e) + { + wxMessageBox(wxString(_("Error reading file:")) + wxT(" \"") + languageFile + wxT("\"") + wxT("\n\n") + + wxT("Row: ") + zen::toStringSep(e.row) + wxT("\n") + + wxT("Column: ") + zen::toStringSep(e.col) + wxT("\n"), _("Error"), wxOK | wxICON_ERROR); + } + catch (PluralForm::ParsingError&) + { + wxMessageBox(wxT("Invalid Plural Form"), _("Error"), wxOK | wxICON_ERROR); + } + } +} + + + +int zen::getLanguage() +{ + FFSLocale* loc = dynamic_cast<FFSLocale*>(zen::getTranslator()); + return loc ? loc->langId() : wxLANGUAGE_ENGLISH_US; +} + + +int zen::retrieveSystemLanguage() +{ + return mapLanguageDialect(static_cast<wxLanguage>(wxLocale::GetSystemLanguage())); +} + + +const std::vector<ExistingTranslations::Entry>& ExistingTranslations::get() +{ + static ExistingTranslations instance; + return instance.locMapping; +} |