summaryrefslogtreecommitdiff
path: root/lib/localization.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/localization.cpp')
-rw-r--r--lib/localization.cpp433
1 files changed, 433 insertions, 0 deletions
diff --git a/lib/localization.cpp b/lib/localization.cpp
new file mode 100644
index 00000000..774ee9ec
--- /dev/null
+++ b/lib/localization.cpp
@@ -0,0 +1,433 @@
+// **************************************************************************
+// * 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 "parse_plural.h"
+#include "parse_lng.h"
+#include <wx+/format_unit.h>
+#include <zen/string_tools.h>
+#include <zen/file_traverser.h>
+#include "ffs_paths.h"
+#include <wx+/string_conv.h>
+#include <zenxml/io.h>
+#include <zen/i18n.h>
+
+using namespace zen;
+
+
+namespace
+{
+//global objects
+std::wstring THOUSANDS_SEPARATOR = L",";
+
+
+class FFSLocale : public TranslationHandler
+{
+public:
+ FFSLocale(const wxString& filename, wxLanguage languageId); //throw (lngfile::ParsingError, PluralForm::ParsingError)
+
+ wxLanguage langId() const { return langId_; }
+
+ virtual std::wstring thousandsSeparator() { return THOUSANDS_SEPARATOR; };
+
+ virtual std::wstring translate(const std::wstring& 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 std::wstring translate(const std::wstring& singular, const std::wstring& 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<std::wstring, std::wstring> Translation;
+ typedef std::map<std::pair<std::wstring, std::wstring>, std::vector<std::wstring> > TranslationPlural;
+
+ Translation transMapping; //map original text |-> translation
+ TranslationPlural transMappingPl;
+ std::unique_ptr<PluralForm> pluralParser;
+ wxLanguage langId_;
+};
+
+
+
+FFSLocale::FFSLocale(const wxString& filename, wxLanguage languageId) : langId_(languageId) //throw (lngfile::ParsingError, PluralForm::ParsingError)
+{
+ std::string inputStream;
+ try
+ {
+ inputStream = loadStream(filename);; //throw XmlFileError
+ }
+ catch (...)
+ {
+ 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 std::wstring original = utf8CvrtTo<std::wstring>(i->first);
+ const std::wstring translation = utf8CvrtTo<std::wstring>(i->second);
+ assert(!translation.empty());
+ transMapping.insert(std::make_pair(original , translation));
+ }
+
+ for (lngfile::TranslationPluralMap::const_iterator i = transPluralInput.begin(); i != transPluralInput.end(); ++i)
+ {
+ const std::wstring singular = utf8CvrtTo<std::wstring>(i->first.first);
+ const std::wstring plural = utf8CvrtTo<std::wstring>(i->first.second);
+ const lngfile::PluralForms& plForms = i->second;
+
+ std::vector<std::wstring> plFormsWide;
+ for (lngfile::PluralForms::const_iterator j = plForms.begin(); j != plForms.end(); ++j)
+ plFormsWide.push_back(utf8CvrtTo<std::wstring>(*j));
+
+ 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 (endsWith(fullName, 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 Int2Type<ReturnValDir::TRAVERSING_DIR_IGNORE>(); }
+ virtual HandleError onError(const std::wstring& errorText) { return TRAV_ERROR_IGNORE; } //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(toZ(zen::getResourceDir() + wxT("Languages")), //throw();
+ false, //don't follow symlinks
+ traverseCallback);
+
+ for (auto i = lngFiles.begin(); i != lngFiles.end(); ++i)
+ try
+ {
+ std::string stream = loadStream(*i);; //throw XmlFileError
+ try
+ {
+ lngfile::TransHeader lngHeader;
+ lngfile::parseHeader(stream, lngHeader); //throw ParsingError
+
+ const wxLanguageInfo* locInfo = wxLocale::FindLanguageInfo(utf8CvrtTo<wxString>(lngHeader.localeName));
+ if (locInfo)
+ {
+ ExistingTranslations::Entry newEntry;
+ newEntry.languageID = locInfo->Language;
+ newEntry.languageName = utf8CvrtTo<wxString>(lngHeader.languageName);
+ newEntry.languageFile = toWx(*i);
+ newEntry.translatorName = utf8CvrtTo<wxString>(lngHeader.translatorName);
+ newEntry.languageFlag = utf8CvrtTo<wxString>(lngHeader.flagFile);
+ locMapping.push_back(newEntry);
+ }
+ }
+ catch (lngfile::ParsingError&) {}
+ }
+ catch (...) {}
+
+ 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_DANISH:
+ //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;
+ }
+}
+}
+
+
+class CustomLocale
+{
+public:
+ CustomLocale(int selectedLng)
+ {
+ const wxLanguageInfo* sysLngInfo = wxLocale::GetLanguageInfo(wxLocale::GetSystemLanguage());
+ const wxLanguageInfo* selLngInfo = wxLocale::GetLanguageInfo(selectedLng);
+
+ bool sysLangIsRTL = sysLngInfo ? sysLngInfo->LayoutDirection == wxLayout_RightToLeft : false;
+ bool selectedLangIsRTL = selLngInfo ? selLngInfo->LayoutDirection == wxLayout_RightToLeft : false;
+
+ if (sysLangIsRTL == selectedLangIsRTL)
+ loc.Init(wxLANGUAGE_DEFAULT); //use sys-lang to preserve sub-language specific rules (e.g. german swiss number punctation)
+ else
+ loc.Init(selectedLng);
+
+ //::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 = utf8CvrtTo<wxString>(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)
+};
+
+
+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;
+ }
+
+
+ //handle RTL swapping: we need wxWidgets to do this
+ static std::unique_ptr<CustomLocale> dummy;
+ dummy.reset(); //avoid global locale lifetime overlap! wxWidgets cannot handle this and will crash!
+ dummy.reset(new CustomLocale(languageFile.empty() ? wxLANGUAGE_ENGLISH : language));
+
+
+ //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;
+}
bgstack15