From 779f2fa451ac759d9de88820898025ce7c71ec30 Mon Sep 17 00:00:00 2001 From: R4SAS Date: Tue, 25 May 2021 22:03:29 +0300 Subject: [PATCH] [i18n] rework localization system Signed-off-by: R4SAS --- daemon/Daemon.cpp | 6 ++-- i18n/English.cpp | 52 +++++++++++++++------------------- i18n/I18N.h | 38 ++++++++++--------------- i18n/I18N_langs.h | 59 +++++++++++++++++++++++++++++---------- i18n/Russian.cpp | 46 +++++++++++++----------------- libi2pd/RouterContext.cpp | 7 +---- libi2pd/RouterContext.h | 12 ++++---- 7 files changed, 111 insertions(+), 109 deletions(-) diff --git a/daemon/Daemon.cpp b/daemon/Daemon.cpp index ec62f5d6..f7f2ef2f 100644 --- a/daemon/Daemon.cpp +++ b/daemon/Daemon.cpp @@ -32,6 +32,7 @@ #include "UPnP.h" #include "Timestamp.h" #include "util.h" +#include "I18N.h" namespace i2p { @@ -345,10 +346,7 @@ namespace util } std::string httpLang; i2p::config::GetOption("http.lang", httpLang); - if (!httpLang.compare("russian")) - i2p::context.SetLanguage (eRussian); - else - i2p::context.SetLanguage (eEnglish); + i2p::i18n::SetLanguage(httpLang); return true; } diff --git a/i18n/English.cpp b/i18n/English.cpp index 8664a0f0..8b13279a 100644 --- a/i18n/English.cpp +++ b/i18n/English.cpp @@ -1,23 +1,34 @@ +/* +* Copyright (c) 2021, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include +#include +#include "I18N.h" -// Russian localization file - -namespace i2p { -namespace i18n { -namespace english { // language +// English 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) { + static int plural (int n) { return n != 1 ? 1 : 0; } static std::map strings { - {"Enabled", "Enabled"}, - {"Disabled", "Disabled"} + {"", ""}, }; static std::map> plurals @@ -25,30 +36,13 @@ namespace english { // language {"days", {"day", "days"}}, {"hours", {"hour", "hours"}}, {"minutes", {"minute", "minutes"}}, - {"seconds", {"second", "seconds"}} + {"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) + std::shared_ptr GetLocale() { - auto it = plurals.find(arg); - if (it == plurals.end()) - { - return arg; - } else { - int form = plural(n); - return it->second[form]; - } + return std::make_shared(strings, plurals, [] (int n)->int { return plural(n); }); } } // language diff --git a/i18n/I18N.h b/i18n/I18N.h index 8ce2aa84..ccb8f413 100644 --- a/i18n/I18N.h +++ b/i18n/I18N.h @@ -11,37 +11,27 @@ #include "RouterContext.h" - -namespace i2p { -namespace i18n { +namespace i2p +{ +namespace i18n +{ + inline void SetLanguage(const std::string &lang) + { + if (!lang.compare("russian")) + i2p::context.SetLanguage (i2p::i18n::russian::GetLocale()); + else + i2p::context.SetLanguage (i2p::i18n::english::GetLocale()); + } inline std::string translate (const std::string& arg) { - switch (i2p::context.GetLanguage ()) - { - case eEnglish: - return i2p::i18n::english::GetString (arg); - case eRussian: - return i2p::i18n::russian::GetString (arg); - default: - return arg; - } + return i2p::context.GetLanguage ()->GetString (arg); } - template - std::string translate (const std::string& arg, inttype&& n) + inline std::string translate (const std::string& arg, const 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; - } + return i2p::context.GetLanguage ()->GetPlural (arg, n); } - } // i18n } // i2p diff --git a/i18n/I18N_langs.h b/i18n/I18N_langs.h index 24c683b4..07bdc087 100644 --- a/i18n/I18N_langs.h +++ b/i18n/I18N_langs.h @@ -9,24 +9,55 @@ #ifndef __I18N_LANGS_H__ #define __I18N_LANGS_H__ -namespace i2p { +namespace i2p +{ +namespace i18n +{ + class Locale + { + public: + Locale ( + const std::map& strings, + const std::map>& plurals, + std::function formula + ): m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { }; -enum Lang { - eEnglish = 0, - eRussian -}; + std::string GetString (const std::string& arg) const + { + const auto it = m_Strings.find(arg); + if (it == m_Strings.end()) + { + return arg; + } + else + { + return it->second; + } + } -namespace i18n { + std::string GetPlural (const std::string& arg, const int& n) const + { + const auto it = m_Plurals.find(arg); + if (it == m_Plurals.end()) + { + return arg; + } + else + { + int form = m_Formula(n); + return it->second[form]; + } + } - namespace english { - std::string GetString (std::string arg); - std::string GetPlural (std::string arg, int n); - } + private: + const std::map m_Strings; + const std::map> m_Plurals; + std::function m_Formula; + }; - namespace russian { - std::string GetString (std::string arg); - std::string GetPlural (std::string arg, int n); - } + // Add localization here with language name as namespace + namespace english { std::shared_ptr GetLocale (); } + namespace russian { std::shared_ptr GetLocale (); } } // i18n } // i2p diff --git a/i18n/Russian.cpp b/i18n/Russian.cpp index 489bee7b..1f2c25da 100644 --- a/i18n/Russian.cpp +++ b/i18n/Russian.cpp @@ -1,16 +1,28 @@ +/* +* Copyright (c) 2021, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + #include #include #include +#include +#include "I18N.h" // Russian localization file -namespace i2p { -namespace i18n { -namespace russian { // language - +namespace i2p +{ +namespace i18n +{ +namespace russian // language +{ // See for language plural forms here: // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html - int plural (int n) { + static int plural (int n) { return n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2; } @@ -218,30 +230,12 @@ namespace russian { // language {"hours", {"час", "часа", "часов"}}, {"minutes", {"минуту", "минуты", "минут"}}, {"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) + std::shared_ptr GetLocale() { - auto it = plurals.find(arg); - if (it == plurals.end()) - { - return arg; - } else { - int form = plural(n); - return it->second[form]; - } + return std::make_shared(strings, plurals, [] (int n)->int { return plural(n); }); } } // language diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index 78f63137..664b457c 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_Language (eEnglish) + m_Error (eRouterErrorNone), m_NetID (I2PD_NET_ID) { } @@ -910,11 +910,6 @@ 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 1cb77a74..ee34c2d8 100644 --- a/libi2pd/RouterContext.h +++ b/libi2pd/RouterContext.h @@ -99,7 +99,7 @@ namespace garlic bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data); void UpdatePort (int port); // called from Daemon - void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon + void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon void PublishNTCP2Address (int port, bool publish, bool v4, bool v6, bool ygg); void UpdateNTCP2Address (bool enable); void RemoveNTCPAddress (bool v4only = true); // delete NTCP address for older routers. TODO: remove later @@ -125,11 +125,11 @@ namespace garlic void SetSupportsMesh (bool supportsmesh, const boost::asio::ip::address_v6& host); bool IsECIES () const { return GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; }; std::unique_ptr& GetCurrentNoiseState () { return m_CurrentNoiseState; }; - + void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove void UpdateStats (); void UpdateTimestamp (uint64_t ts); // in seconds, called from NetDb before publishing - void CleanupDestination (); // garlic destination + void CleanupDestination (); // garlic destination // implements LocalDestination std::shared_ptr GetIdentity () const { return m_Keys.GetPublic (); }; @@ -146,8 +146,8 @@ namespace garlic void ProcessDeliveryStatusMessage (std::shared_ptr msg); // i18n - Lang GetLanguage () const { return m_Language; }; - void SetLanguage (Lang language); + std::shared_ptr GetLanguage () { return m_Language; }; + void SetLanguage (const std::shared_ptr language) { m_Language = language; }; protected: @@ -185,7 +185,7 @@ namespace garlic std::unique_ptr m_InitialNoiseState, m_CurrentNoiseState; // i18n - Lang m_Language; + std::shared_ptr m_Language; }; extern RouterContext context;