From 80b44fc9a9c6a90872ccaa1988737bda67f73e3f Mon Sep 17 00:00:00 2001 From: R4SAS Date: Sat, 22 May 2021 18:29:05 +0300 Subject: [PATCH] Support multilang, update code Signed-off-by: R4SAS --- contrib/i2pd.conf | 3 +++ daemon/Daemon.cpp | 7 +++++ daemon/HTTPServer.cpp | 23 +--------------- i18n/English.cpp | 56 +++++++++++++++++++++++++++++++++++++++ i18n/I18N.h | 21 +++++++++++++-- libi2pd/Config.cpp | 1 + libi2pd/FS.cpp | 13 +++++++++ libi2pd/FS.h | 3 +++ libi2pd/RouterContext.cpp | 7 ++++- libi2pd/RouterContext.h | 18 ++++++++++--- 10 files changed, 124 insertions(+), 28 deletions(-) create mode 100644 i18n/English.cpp diff --git a/contrib/i2pd.conf b/contrib/i2pd.conf index e8c397f5..3c9c71ff 100644 --- a/contrib/i2pd.conf +++ b/contrib/i2pd.conf @@ -103,6 +103,9 @@ port = 7070 # auth = true # user = i2pd # pass = changeme +## Select webconsole language +## Currently supported only english (default) and russian languages +# lang = english [httpproxy] ## Uncomment and set to 'false' to disable HTTP Proxy diff --git a/daemon/Daemon.cpp b/daemon/Daemon.cpp index 77000652..ec62f5d6 100644 --- a/daemon/Daemon.cpp +++ b/daemon/Daemon.cpp @@ -343,6 +343,13 @@ namespace util LogPrint(eLogInfo, "Daemon: using hidden mode"); i2p::data::netdb.SetHidden(true); } + + std::string httpLang; i2p::config::GetOption("http.lang", httpLang); + if (!httpLang.compare("russian")) + i2p::context.SetLanguage (eRussian); + else + i2p::context.SetLanguage (eEnglish); + return true; } diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index 043d1630..87744f37 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -32,11 +32,6 @@ #include "ECIESX25519AEADRatchetSession.h" #include "I18N.h" -#ifdef _WIN32 -#include -#include -#endif - #ifdef WIN32_APP #include "Win32App.h" #endif @@ -136,20 +131,6 @@ namespace http { static std::string ConvertTime (uint64_t time); std::map HTTPConnection::m_Tokens; - std::string DataPath; - - static void SetDataDir () - { -#ifdef _WIN32 - boost::filesystem::wpath path (i2p::fs::GetDataDir()); - auto loc = boost::filesystem::path::imbue(std::locale( std::locale(), new std::codecvt_utf8_utf16() ) ); - i2p::http::DataPath = path.string(); - boost::filesystem::path::imbue(loc); // Return it back -#else - i2p::http::DataPath = i2p::fs::GetDataDir(); -#endif - } - static void ShowUptime (std::stringstream& s, int seconds) { int num; @@ -331,7 +312,7 @@ namespace http { s << "Transit: "; ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ()); s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " KiB/s)
\r\n"; - s << "Data path: " << i2p::http::DataPath << "
\r\n"; + s << "Data path: " << i2p::fs::GetUTF8DataDir() << "
\r\n"; s << "
"; if((outputFormat==OutputFormatEnum::forWebConsole)||!includeHiddenContent) { s << "\r\n\r\n
\r\n"; @@ -1390,8 +1371,6 @@ namespace http { LogPrint(eLogInfo, "HTTPServer: password set to ", pass); } - i2p::http::SetDataDir(); - m_IsRunning = true; m_Thread.reset (new std::thread (std::bind (&HTTPServer::Run, this))); m_Acceptor.listen (); diff --git a/i18n/English.cpp b/i18n/English.cpp new file mode 100644 index 00000000..8664a0f0 --- /dev/null +++ b/i18n/English.cpp @@ -0,0 +1,56 @@ +#include +#include +#include + +// Russian localization file + +namespace i2p { +namespace i18n { +namespace english { // language + + // See for language plural forms here: + // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html + int plural (int n) { + return n != 1 ? 1 : 0; + } + + static std::map strings + { + {"Enabled", "Enabled"}, + {"Disabled", "Disabled"} + }; + + static std::map> plurals + { + {"days", {"day", "days"}}, + {"hours", {"hour", "hours"}}, + {"minutes", {"minute", "minutes"}}, + {"seconds", {"second", "seconds"}} + }; + + std::string GetString (std::string arg) + { + auto it = strings.find(arg); + if (it == strings.end()) + { + return arg; + } else { + return it->second; + } + } + + std::string GetPlural (std::string arg, int n) + { + auto it = plurals.find(arg); + if (it == plurals.end()) + { + return arg; + } else { + int form = plural(n); + return it->second[form]; + } + } + +} // language +} // i18n +} // i2p diff --git a/i18n/I18N.h b/i18n/I18N.h index 4556fe25..eddec514 100644 --- a/i18n/I18N.h +++ b/i18n/I18N.h @@ -9,9 +9,16 @@ #ifndef __I18N_H__ #define __I18N_H__ +#include "RouterContext.h" + namespace i2p { namespace i18n { + namespace english { + std::string GetString (std::string arg); + std::string GetPlural (std::string arg, int n); + } + namespace russian { std::string GetString (std::string arg); std::string GetPlural (std::string arg, int n); @@ -19,13 +26,23 @@ namespace i18n { std::string translate (std::string arg) { - return i2p::i18n::russian::GetString (arg); + switch (i2p::context.GetLanguage ()) + { + case eEnglish: return i2p::i18n::english::GetString (arg); + case eRussian: return i2p::i18n::russian::GetString (arg); + default: return arg; + } } template std::string translate (std::string arg, inttype&& n) { - return i2p::i18n::russian::GetPlural (arg, (int) n); + switch (i2p::context.GetLanguage ()) + { + case eEnglish: return i2p::i18n::english::GetPlural (arg, (int) n); + case eRussian: return i2p::i18n::russian::GetPlural (arg, (int) n); + default: return arg; + } } } // i18n diff --git a/libi2pd/Config.cpp b/libi2pd/Config.cpp index 5b584e35..f5316860 100644 --- a/libi2pd/Config.cpp +++ b/libi2pd/Config.cpp @@ -93,6 +93,7 @@ namespace config { ("http.strictheaders", value()->default_value(true), "Enable strict host checking on WebUI") ("http.hostname", value()->default_value("localhost"), "Expected hostname for WebUI") ("http.webroot", value()->default_value("/"), "WebUI root path (default: / )") + ("http.lang", value()->default_value("english"), "WebUI language (default: english )") ; options_description httpproxy("HTTP Proxy options"); diff --git a/libi2pd/FS.cpp b/libi2pd/FS.cpp index 250b24ef..6ac302b0 100644 --- a/libi2pd/FS.cpp +++ b/libi2pd/FS.cpp @@ -12,6 +12,7 @@ #ifdef _WIN32 #include #include +#include #endif #include "Base.h" @@ -41,6 +42,18 @@ namespace fs { return dataDir; } + const std::string GetUTF8DataDir () { +#ifdef _WIN32 + boost::filesystem::wpath path (dataDir); + auto loc = boost::filesystem::path::imbue(std::locale( std::locale(), new std::codecvt_utf8_utf16() ) ); // convert path to UTF-8 + auto dataDirUTF8 = path.string(); + boost::filesystem::path::imbue(loc); // Return locale settings back + return dataDirUTF8; +#else + return dataDir; // linux, osx, android uses UTF-8 by default +#endif + } + void DetectDataDir(const std::string & cmdline_param, bool isService) { if (cmdline_param != "") { dataDir = cmdline_param; diff --git a/libi2pd/FS.h b/libi2pd/FS.h index 698e9b6b..f07ee35c 100644 --- a/libi2pd/FS.h +++ b/libi2pd/FS.h @@ -75,6 +75,9 @@ namespace fs { /** @brief Returns datadir path */ const std::string & GetDataDir(); + /** @brief Returns datadir path in UTF-8 encoding */ + const std::string GetUTF8DataDir(); + /** * @brief Set datadir either from cmdline option or using autodetection * @param cmdline_param Value of cmdline parameter --datadir= diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index ad6576ed..0cfcb18a 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -29,7 +29,7 @@ namespace i2p RouterContext::RouterContext (): m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false), m_ShareRatio (100), m_Status (eRouterStatusUnknown), m_StatusV6 (eRouterStatusUnknown), - m_Error (eRouterErrorNone), m_NetID (I2PD_NET_ID) + m_Error (eRouterErrorNone), m_NetID (I2PD_NET_ID), m_Language (eEnglish) { } @@ -916,6 +916,11 @@ namespace i2p } } + void RouterContext::SetLanguage (Lang language) + { + m_Language = language; + } + i2p::crypto::X25519Keys& RouterContext::GetStaticKeys () { if (!m_StaticKeys) diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h index a4d18f82..dad3fdca 100644 --- a/libi2pd/RouterContext.h +++ b/libi2pd/RouterContext.h @@ -24,7 +24,7 @@ namespace i2p namespace garlic { class RouterIncomingRatchetSession; -} +} const char ROUTER_INFO[] = "router.info"; const char ROUTER_KEYS[] = "router.keys"; @@ -39,7 +39,7 @@ namespace garlic eRouterStatusError = 3, eRouterStatusUnknown = 4, eRouterStatusProxy = 5, - eRouterStatusMesh = 6 + eRouterStatusMesh = 6 }; enum RouterError @@ -49,7 +49,12 @@ namespace garlic eRouterErrorOffline = 2, eRouterErrorSymmetricNAT = 3 }; - + + enum Lang { + eEnglish = 0, + eRussian + }; + class RouterContext: public i2p::garlic::GarlicDestination { private: @@ -144,6 +149,10 @@ namespace garlic void ProcessGarlicMessage (std::shared_ptr msg); void ProcessDeliveryStatusMessage (std::shared_ptr msg); + // i18n + Lang GetLanguage () const { return m_Language; }; + void SetLanguage (Lang language); + protected: // implements GarlicDestination @@ -178,6 +187,9 @@ namespace garlic std::unique_ptr m_StaticKeys; // for ECIESx25519 std::unique_ptr m_InitialNoiseState, m_CurrentNoiseState; + + // i18n + Lang m_Language; }; extern RouterContext context;