diff --git a/CMakeLists.txt b/CMakeLists.txt index 079b2335..02ff01b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -247,6 +247,8 @@ endif() install(DIRECTORY webskins DESTINATION "${CMAKE_INSTALL_DATADIR}/znc") +install(DIRECTORY translations + DESTINATION "${CMAKE_INSTALL_DATADIR}/znc") install(DIRECTORY man/ DESTINATION "${CMAKE_INSTALL_MANDIR}/man1" FILES_MATCHING PATTERN "znc*") diff --git a/include/znc/Translation.h b/include/znc/Translation.h index 0ea4864c..8ecfd2c2 100644 --- a/include/znc/Translation.h +++ b/include/znc/Translation.h @@ -20,6 +20,12 @@ #include #include +struct CTranslationInfo { + static std::map GetTranslations(); + + CString sSelfName; +}; + // All instances of modules share single message map using this class stored in // CZNC. class CTranslation { @@ -38,6 +44,7 @@ class CTranslation { void DelReference(const CString& sDomain); private: + // Domain is either "znc" or "znc-foo" where foo is a module name const std::locale& LoadTranslation(const CString& sDomain); std::unordered_map> diff --git a/modules/controlpanel.cpp b/modules/controlpanel.cpp index 5472ef74..e26be904 100644 --- a/modules/controlpanel.cpp +++ b/modules/controlpanel.cpp @@ -276,7 +276,9 @@ class CAdminMod : public CModule { PutModule("StatusPrefix = " + pUser->GetStatusPrefix()); #ifdef HAVE_I18N else if (sVar == "language") - PutModule("Language = " + pUser->GetLanguage()); + PutModule("Language = " + (pUser->GetLanguage().empty() + ? "en" + : pUser->GetLanguage())); #endif #ifdef HAVE_ICU else if (sVar == "clientencoding") @@ -453,8 +455,22 @@ class CAdminMod : public CModule { } #ifdef HAVE_I18N else if (sVar == "language") { - pUser->SetLanguage(sValue); - PutModule("Language = " + pUser->GetLanguage()); + auto mTranslations = CTranslationInfo::GetTranslations(); + // TODO: maybe stop special-casing English + if (sValue == "en") { + pUser->SetLanguage(""); + PutModule("Language is set to English"); + } else if (mTranslations.count(sValue)) { + pUser->SetLanguage(sValue); + PutModule("Language = " + sValue); + } else { + VCString vsCodes = {"en"}; + for (const auto it : mTranslations) { + vsCodes.push_back(it.first); + } + PutModule("Supported languages: " + + CString(", ").Join(vsCodes.begin(), vsCodes.end())); + } } #endif #ifdef HAVE_ICU diff --git a/modules/webadmin.cpp b/modules/webadmin.cpp index 966c0a62..b44342a6 100644 --- a/modules/webadmin.cpp +++ b/modules/webadmin.cpp @@ -316,7 +316,11 @@ class CWebAdminMod : public CModule { pNewUser->SetNoTrafficTimeout(uNoTrafficTimeout); #ifdef HAVE_I18N - pNewUser->SetLanguage(WebSock.GetParam("language")); + CString sLanguage = WebSock.GetParam("language"); + if (CTranslationInfo::GetTranslations().count(sLanguage) == 0) { + sLanguage = ""; + } + pNewUser->SetLanguage(sLanguage); #endif #ifdef HAVE_ICU CString sEncodingUtf = WebSock.GetParam("encoding_utf"); @@ -1376,13 +1380,15 @@ class CWebAdminMod : public CModule { #ifdef HAVE_I18N Tmpl["HaveI18N"] = "true"; - // TODO don't have them hardcoded here + // TODO maybe stop hardcoding English here CTemplate& l_en = Tmpl.AddRow("LanguageLoop"); l_en["Code"] = ""; l_en["Name"] = "English"; - CTemplate& l_ru = Tmpl.AddRow("LanguageLoop"); - l_ru["Code"] = "ru-RU"; - l_ru["Name"] = "Russian"; + for (const auto& it : CTranslationInfo::GetTranslations()) { + CTemplate& lang = Tmpl.AddRow("LanguageLoop"); + lang["Code"] = it.first; + lang["Name"] = it.second.sSelfName; + } #else Tmpl["HaveI18N"] = "false"; #endif diff --git a/src/Translation.cpp b/src/Translation.cpp index dbde1268..cc7e1d6c 100644 --- a/src/Translation.cpp +++ b/src/Translation.cpp @@ -15,11 +15,36 @@ */ #include +#include #ifdef HAVE_I18N #include #endif +namespace { +std::map FillTranslations() { + std::map mTranslations; + CDir Dir; + Dir.Fill(_DATADIR_ "/translations"); + for (CFile* pFile : Dir) { + CString sName = pFile->GetShortName(); + CTranslationInfo& translation = mTranslations[sName]; + MCString msData; + // TODO: make the file format more sensible than this + msData.ReadFromDisk(pFile->GetLongName()); + translation.sSelfName = msData["SelfName"]; + // TODO: right-to-left support + } + return mTranslations; +} +} // namespace + +std::map CTranslationInfo::GetTranslations() { + static std::map mTranslations = + FillTranslations(); + return mTranslations; +} + CTranslation& CTranslation::Get() { static CTranslation translation; return translation; diff --git a/translations/ru-RU b/translations/ru-RU new file mode 100644 index 00000000..8076c1ad --- /dev/null +++ b/translations/ru-RU @@ -0,0 +1 @@ +SelfName Русский