mirror of
https://github.com/znc/znc.git
synced 2026-05-10 07:14:43 +02:00
Add framework for translating ZNC to different languages
This commit is contained in:
+10
-4
@@ -19,13 +19,13 @@ if(CMAKE_VERSION VERSION_LESS 3.2)
|
||||
set_source_files_properties("versionc.cpp" PROPERTIES GENERATED true)
|
||||
endif()
|
||||
|
||||
znc_add_library(znclib ${lib_type} "ZNCString.cpp" "znc.cpp" "IRCNetwork.cpp"
|
||||
set(znc_cpp "ZNCString.cpp" "znc.cpp" "IRCNetwork.cpp" "Translation.cpp"
|
||||
"IRCSock.cpp" "Client.cpp" "Chan.cpp" "Nick.cpp" "Server.cpp"
|
||||
"Modules.cpp" "MD5.cpp" "Buffer.cpp" "Utils.cpp" "FileUtils.cpp"
|
||||
"HTTPSock.cpp" "Template.cpp" "ClientCommand.cpp" "Socket.cpp"
|
||||
"SHA256.cpp" "WebModules.cpp" "Listener.cpp" "Config.cpp" "ZNCDebug.cpp"
|
||||
"Threads.cpp" "Query.cpp" "SSLVerifyHost.cpp" "Message.cpp" "Csocket.cpp"
|
||||
"versionc.cpp" "User.cpp")
|
||||
"Threads.cpp" "Query.cpp" "SSLVerifyHost.cpp" "Message.cpp" "User.cpp")
|
||||
znc_add_library(znclib ${lib_type} ${znc_cpp} "Csocket.cpp" "versionc.cpp")
|
||||
znc_add_executable(znc "main.cpp")
|
||||
target_link_libraries(znc PRIVATE znclib)
|
||||
|
||||
@@ -76,6 +76,10 @@ if(ICU_FOUND)
|
||||
target_link_libraries(znclib ${ICU_LDFLAGS})
|
||||
list(APPEND znc_include_dirs ${ICU_INCLUDE_DIRS})
|
||||
endif()
|
||||
if(Boost_FOUND)
|
||||
target_link_libraries(znclib ${Boost_LIBRARIES})
|
||||
list(APPEND znc_include_dirs ${Boost_INCLUDE_DIRS})
|
||||
endif()
|
||||
target_include_directories(znc PUBLIC ${znc_include_dirs})
|
||||
target_include_directories(znclib PUBLIC ${znc_include_dirs})
|
||||
|
||||
@@ -91,7 +95,9 @@ set_target_properties(znclib PROPERTIES
|
||||
OUTPUT_NAME "znc"
|
||||
SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}")
|
||||
|
||||
|
||||
if(HAVE_I18N)
|
||||
add_subdirectory(po)
|
||||
endif()
|
||||
|
||||
|
||||
install(TARGETS znc ${install_lib}
|
||||
|
||||
@@ -94,6 +94,7 @@ void CClient::SendRequiredPasswordNotice() {
|
||||
}
|
||||
|
||||
void CClient::ReadLine(const CString& sData) {
|
||||
CLanguageScope user_lang(GetUser() ? GetUser()->GetLanguage() : "");
|
||||
CString sLine = sData;
|
||||
|
||||
sLine.TrimRight("\n\r");
|
||||
|
||||
+61
-21
@@ -138,6 +138,7 @@ CModule::CModule(ModHandle pDLL, CUser* pUser, CIRCNetwork* pNetwork,
|
||||
m_sSavePath(""),
|
||||
m_sArgs(""),
|
||||
m_sModPath(""),
|
||||
m_Translation("znc-" + sModName),
|
||||
m_mssRegistry(),
|
||||
m_vSubPages(),
|
||||
m_mCommands() {
|
||||
@@ -516,6 +517,13 @@ bool CModule::AddCommand(const CString& sCmd, const CString& sArgs,
|
||||
return AddCommand(std::move(cmd));
|
||||
}
|
||||
|
||||
bool CModule::AddCommand(const CString& sCmd, const CString& sArgs,
|
||||
const CDelayedTranslation& dDesc,
|
||||
std::function<void(const CString& sLine)> func) {
|
||||
CModCommand cmd(sCmd, std::move(func), sArgs, dDesc);
|
||||
return AddCommand(std::move(cmd));
|
||||
}
|
||||
|
||||
void CModule::AddHelpCommand() {
|
||||
AddCommand("Help", &CModule::HandleHelpCommand, "search",
|
||||
"Generate this output");
|
||||
@@ -1595,6 +1603,8 @@ bool CModules::LoadModule(const CString& sModule, const CString& sArgs,
|
||||
sRetMsg = "Unable to find module [" + sModule + "]";
|
||||
return false;
|
||||
}
|
||||
Info.SetName(sModule);
|
||||
Info.SetPath(sModPath);
|
||||
|
||||
ModHandle p =
|
||||
OpenModule(sModule, sModPath, bVersionMismatch, Info, sRetMsg);
|
||||
@@ -1754,14 +1764,13 @@ bool CModules::GetModPathInfo(CModInfo& ModInfo, const CString& sModule,
|
||||
const CString& sModPath, CString& sRetMsg) {
|
||||
bool bVersionMismatch;
|
||||
|
||||
ModHandle p =
|
||||
OpenModule(sModule, sModPath, bVersionMismatch, ModInfo, sRetMsg);
|
||||
|
||||
if (!p) return false;
|
||||
|
||||
ModInfo.SetName(sModule);
|
||||
ModInfo.SetPath(sModPath);
|
||||
|
||||
ModHandle p =
|
||||
OpenModule(sModule, sModPath, bVersionMismatch, ModInfo, sRetMsg);
|
||||
if (!p) return false;
|
||||
|
||||
if (bVersionMismatch) {
|
||||
ModInfo.SetDescription(
|
||||
"--- Version mismatch, recompile this module. ---");
|
||||
@@ -1910,6 +1919,7 @@ ModHandle CModules::OpenModule(const CString& sModule, const CString& sModPath,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CTranslationDomainRefHolder translation("znc-" + sModule);
|
||||
typedef bool (*InfoFP)(double, CModInfo&);
|
||||
InfoFP ZNCModInfo = (InfoFP)dlsym(p, "ZNCModInfo");
|
||||
|
||||
@@ -1930,32 +1940,32 @@ ModHandle CModules::OpenModule(const CString& sModule, const CString& sModPath,
|
||||
return p;
|
||||
}
|
||||
|
||||
CModCommand::CModCommand() : m_sCmd(), m_pFunc(nullptr), m_sArgs(), m_sDesc() {}
|
||||
CModCommand::CModCommand()
|
||||
: m_sCmd(), m_pFunc(nullptr), m_sArgs(), m_sDesc(), m_bTranslating(false) {}
|
||||
|
||||
CModCommand::CModCommand(const CString& sCmd, CModule* pMod, ModCmdFunc func,
|
||||
const CString& sArgs, const CString& sDesc)
|
||||
: m_sCmd(sCmd),
|
||||
m_pFunc([pMod, func](const CString& sLine) { (pMod->*func)(sLine); }),
|
||||
m_sArgs(sArgs),
|
||||
m_sDesc(sDesc) {}
|
||||
m_sDesc(sDesc),
|
||||
m_bTranslating(false) {}
|
||||
|
||||
CModCommand::CModCommand(const CString& sCmd, CmdFunc func,
|
||||
const CString& sArgs, const CString& sDesc)
|
||||
: m_sCmd(sCmd), m_pFunc(std::move(func)), m_sArgs(sArgs), m_sDesc(sDesc) {}
|
||||
: m_sCmd(sCmd),
|
||||
m_pFunc(std::move(func)),
|
||||
m_sArgs(sArgs),
|
||||
m_sDesc(sDesc),
|
||||
m_bTranslating(false) {}
|
||||
|
||||
CModCommand::CModCommand(const CModCommand& other)
|
||||
: m_sCmd(other.m_sCmd),
|
||||
m_pFunc(other.m_pFunc),
|
||||
m_sArgs(other.m_sArgs),
|
||||
m_sDesc(other.m_sDesc) {}
|
||||
|
||||
CModCommand& CModCommand::operator=(const CModCommand& other) {
|
||||
m_sCmd = other.m_sCmd;
|
||||
m_pFunc = other.m_pFunc;
|
||||
m_sArgs = other.m_sArgs;
|
||||
m_sDesc = other.m_sDesc;
|
||||
return *this;
|
||||
}
|
||||
CModCommand::CModCommand(const CString& sCmd, CmdFunc func,
|
||||
const CString& sArgs, const CDelayedTranslation& dDesc)
|
||||
: m_sCmd(sCmd),
|
||||
m_pFunc(std::move(func)),
|
||||
m_sArgs(sArgs),
|
||||
m_dDesc(dDesc),
|
||||
m_bTranslating(true) {}
|
||||
|
||||
void CModCommand::InitHelp(CTable& Table) {
|
||||
Table.AddColumn("Command");
|
||||
@@ -1967,3 +1977,33 @@ void CModCommand::AddHelp(CTable& Table) const {
|
||||
Table.SetCell("Command", GetCommand() + " " + GetArgs());
|
||||
Table.SetCell("Description", GetDescription());
|
||||
}
|
||||
|
||||
CString CModCommand::GetDescription() const {
|
||||
return m_bTranslating ? m_dDesc.Resolve() : m_sDesc;
|
||||
}
|
||||
|
||||
CString CModule::t(const CString& sEnglish, const CString& sContext) const {
|
||||
return CTranslation::Get().Singular("znc-" + GetModName(), sContext,
|
||||
sEnglish);
|
||||
}
|
||||
|
||||
CInlineFormatMessage CModule::f(const CString& sEnglish,
|
||||
const CString& sContext) const {
|
||||
return CInlineFormatMessage(t(sEnglish, sContext));
|
||||
}
|
||||
|
||||
CInlineFormatMessage CModule::p(const CString& sEnglish,
|
||||
const CString& sEnglishes, int iNum,
|
||||
const CString& sContext) const {
|
||||
return CInlineFormatMessage(CTranslation::Get().Plural(
|
||||
"znc-" + GetModName(), sContext, sEnglish, sEnglishes, iNum));
|
||||
}
|
||||
|
||||
CDelayedTranslation CModule::d(const CString& sEnglish,
|
||||
const CString& sContext) const {
|
||||
return CDelayedTranslation("znc-" + GetModName(), sContext, sEnglish);
|
||||
}
|
||||
|
||||
CString CModInfo::t(const CString& sEnglish, const CString& sContext) const {
|
||||
return CTranslation::Get().Singular("znc-" + GetName(), sContext, sEnglish);
|
||||
}
|
||||
|
||||
@@ -607,3 +607,24 @@ void CIRCSocket::IcuExtFromUCallback(UConverterFromUnicodeArgs* fromArgs,
|
||||
err);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
CString CSocket::t(const CString& sEnglish, const CString& sContext) const {
|
||||
return GetModule()->t(sEnglish, sContext);
|
||||
}
|
||||
|
||||
CInlineFormatMessage CSocket::f(const CString& sEnglish,
|
||||
const CString& sContext) const {
|
||||
return GetModule()->f(sEnglish, sContext);
|
||||
}
|
||||
|
||||
CInlineFormatMessage CSocket::p(const CString& sEnglish,
|
||||
const CString& sEnglishes, int iNum,
|
||||
const CString& sContext) const {
|
||||
return GetModule()->p(sEnglish, sEnglishes, iNum, sContext);
|
||||
}
|
||||
|
||||
CDelayedTranslation CSocket::d(const CString& sEnglish,
|
||||
const CString& sContext) const {
|
||||
return GetModule()->d(sEnglish, sContext);
|
||||
}
|
||||
|
||||
+55
-3
@@ -17,6 +17,7 @@
|
||||
#include <znc/Template.h>
|
||||
#include <znc/FileUtils.h>
|
||||
#include <znc/ZNCDebug.h>
|
||||
#include <znc/Translation.h>
|
||||
#include <algorithm>
|
||||
|
||||
using std::stringstream;
|
||||
@@ -313,6 +314,10 @@ bool CTemplate::Print(const CString& sFileName, ostream& oOut) {
|
||||
bool bLoopBreak = false;
|
||||
bool bExit = false;
|
||||
|
||||
// Single template works across multiple translation domains, e.g. .tmpl of
|
||||
// a module can INC'lude Footer.tmpl from core
|
||||
CString sI18N;
|
||||
|
||||
while (File.ReadLine(sLine)) {
|
||||
CString sOutput;
|
||||
bool bFoundATag = false;
|
||||
@@ -420,6 +425,12 @@ bool CTemplate::Print(const CString& sFileName, ostream& oOut) {
|
||||
} else if (sAction.Equals("SETBLOCK")) {
|
||||
sSetBlockVar = sArgs;
|
||||
bInSetBlock = true;
|
||||
} else if (sAction.Equals("ENDSETBLOCK")) {
|
||||
CString sName = sSetBlockVar.Token(0);
|
||||
(*this)[sName] += sOutput;
|
||||
sOutput = "";
|
||||
bInSetBlock = false;
|
||||
sSetBlockVar = "";
|
||||
} else if (sAction.Equals("EXPAND")) {
|
||||
sOutput += ExpandFile(sArgs, true);
|
||||
} else if (sAction.Equals("VAR")) {
|
||||
@@ -536,6 +547,50 @@ bool CTemplate::Print(const CString& sFileName, ostream& oOut) {
|
||||
}
|
||||
} else if (sAction.Equals("REM")) {
|
||||
uSkip++;
|
||||
} else if (sAction.Equals("I18N")) {
|
||||
sI18N = sArgs;
|
||||
} else if (sAction.Equals("FORMAT") ||
|
||||
sAction.Equals("PLURAL")) {
|
||||
bool bHaveContext = false;
|
||||
if (sArgs.TrimPrefix("CTX=")) {
|
||||
bHaveContext = true;
|
||||
}
|
||||
VCString vsArgs;
|
||||
sArgs.QuoteSplit(vsArgs);
|
||||
CString sEnglish, sEnglishes, sContext;
|
||||
int idx = 0;
|
||||
if (bHaveContext && vsArgs.size() > idx) {
|
||||
sContext = vsArgs[idx];
|
||||
idx++;
|
||||
}
|
||||
if (vsArgs.size() > idx) {
|
||||
sEnglish = vsArgs[idx];
|
||||
idx++;
|
||||
}
|
||||
CString sFormat;
|
||||
if (sAction.Equals("PLURAL")) {
|
||||
CString sEnglishes;
|
||||
int iNum = 0;
|
||||
if (vsArgs.size() > idx) {
|
||||
sEnglishes = vsArgs[idx];
|
||||
idx++;
|
||||
}
|
||||
if (vsArgs.size() > idx) {
|
||||
iNum = GetValue(vsArgs[idx], true).ToInt();
|
||||
idx++;
|
||||
}
|
||||
sFormat = CTranslation::Get().Plural(
|
||||
sI18N, sContext, sEnglish, sEnglishes, iNum);
|
||||
} else {
|
||||
sFormat = CTranslation::Get().Singular(
|
||||
sI18N, sContext, sEnglish);
|
||||
}
|
||||
MCString msParams;
|
||||
for (int i = 0; i + idx < vsArgs.size(); ++i) {
|
||||
msParams[CString(i + 1)] =
|
||||
GetValue(vsArgs[i + idx], false);
|
||||
}
|
||||
sOutput += CString::NamedFormat(sFormat, msParams);
|
||||
} else {
|
||||
bNotFound = true;
|
||||
}
|
||||
@@ -557,9 +612,6 @@ bool CTemplate::Print(const CString& sFileName, ostream& oOut) {
|
||||
if (uSkip) {
|
||||
uSkip--;
|
||||
}
|
||||
} else if (sAction.Equals("ENDSETBLOCK")) {
|
||||
bInSetBlock = false;
|
||||
sSetBlockVar = "";
|
||||
} else if (sAction.Equals("ENDLOOP")) {
|
||||
if (bLoopCont && uSkip == 1) {
|
||||
uSkip--;
|
||||
|
||||
@@ -29,8 +29,6 @@ static const size_t MAX_IDLE_THREADS = 3;
|
||||
static const size_t MAX_TOTAL_THREADS = 20;
|
||||
|
||||
CThreadPool& CThreadPool::Get() {
|
||||
// Beware! The following is not thread-safe! This function must
|
||||
// be called once any thread is started.
|
||||
static CThreadPool pool;
|
||||
return pool;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2004-2016 ZNC, see the NOTICE file for details.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <znc/Translation.h>
|
||||
|
||||
#ifdef HAVE_I18N
|
||||
#include <boost/locale.hpp>
|
||||
#endif
|
||||
|
||||
CTranslation& CTranslation::Get() {
|
||||
static CTranslation translation;
|
||||
return translation;
|
||||
}
|
||||
|
||||
CString CTranslation::Singular(const CString& sDomain, const CString& sContext,
|
||||
const CString& sEnglish) {
|
||||
#ifdef HAVE_I18N
|
||||
const std::locale& loc = LoadTranslation(sDomain);
|
||||
return boost::locale::translate(sContext, sEnglish).str(loc);
|
||||
#else
|
||||
return sEnglish;
|
||||
#endif
|
||||
}
|
||||
CString CTranslation::Plural(const CString& sDomain, const CString& sContext,
|
||||
const CString& sEnglish, const CString& sEnglishes,
|
||||
int iNum) {
|
||||
#ifdef HAVE_I18N
|
||||
const std::locale& loc = LoadTranslation(sDomain);
|
||||
return boost::locale::translate(sContext, sEnglish, sEnglishes, iNum)
|
||||
.str(loc);
|
||||
#else
|
||||
if (iNum == 1) {
|
||||
return sEnglish;
|
||||
} else {
|
||||
return sEnglishes;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
const std::locale& CTranslation::LoadTranslation(const CString& sDomain) {
|
||||
CString sLanguage = m_sLanguageStack.empty() ? "" : m_sLanguageStack.back();
|
||||
#ifdef HAVE_I18N
|
||||
// Not using built-in support for multiple domains in single std::locale
|
||||
// via overloaded call to .str() because we need to be able to reload
|
||||
// translations from disk independently when a module gets updated
|
||||
auto& domain = m_Translations[sDomain];
|
||||
auto lang_it = domain.find(sLanguage);
|
||||
if (lang_it == domain.end()) {
|
||||
boost::locale::generator gen;
|
||||
gen.add_messages_path(LOCALE_DIR);
|
||||
gen.add_messages_domain(sDomain);
|
||||
std::tie(lang_it, std::ignore) =
|
||||
domain.emplace(sLanguage, gen(sLanguage + ".UTF-8"));
|
||||
}
|
||||
return lang_it->second;
|
||||
#else
|
||||
// dummy, it's not used anyway
|
||||
return std::locale::classic();
|
||||
#endif
|
||||
}
|
||||
|
||||
void CTranslation::PushLanguage(const CString& sLanguage) {
|
||||
m_sLanguageStack.push_back(sLanguage);
|
||||
}
|
||||
void CTranslation::PopLanguage() { m_sLanguageStack.pop_back(); }
|
||||
|
||||
void CTranslation::NewReference(const CString& sDomain) {
|
||||
m_miReferences[sDomain]++;
|
||||
}
|
||||
void CTranslation::DelReference(const CString& sDomain) {
|
||||
if (!--m_miReferences[sDomain]) {
|
||||
m_Translations.erase(sDomain);
|
||||
}
|
||||
}
|
||||
|
||||
CString CCoreTranslationMixin::t(const CString& sEnglish,
|
||||
const CString& sContext) {
|
||||
return CTranslation::Get().Singular("znc", sContext, sEnglish);
|
||||
}
|
||||
|
||||
CInlineFormatMessage CCoreTranslationMixin::f(const CString& sEnglish,
|
||||
const CString& sContext) {
|
||||
return CInlineFormatMessage(t(sEnglish, sContext));
|
||||
}
|
||||
|
||||
CInlineFormatMessage CCoreTranslationMixin::p(const CString& sEnglish,
|
||||
const CString& sEnglishes,
|
||||
int iNum,
|
||||
const CString& sContext) {
|
||||
return CInlineFormatMessage(CTranslation::Get().Plural(
|
||||
"znc", sContext, sEnglish, sEnglishes, iNum));
|
||||
}
|
||||
|
||||
CDelayedTranslation CCoreTranslationMixin::d(const CString& sEnglish,
|
||||
const CString& sContext) {
|
||||
return CDelayedTranslation("znc", sContext, sEnglish);
|
||||
}
|
||||
|
||||
|
||||
CLanguageScope::CLanguageScope(const CString& sLanguage) {
|
||||
CTranslation::Get().PushLanguage(sLanguage);
|
||||
}
|
||||
CLanguageScope::~CLanguageScope() { CTranslation::Get().PopLanguage(); }
|
||||
|
||||
CString CDelayedTranslation::Resolve() const {
|
||||
return CTranslation::Get().Singular(m_sDomain, m_sContext, m_sEnglish);
|
||||
}
|
||||
|
||||
CTranslationDomainRefHolder::CTranslationDomainRefHolder(const CString& sDomain)
|
||||
: m_sDomain(sDomain) {
|
||||
CTranslation::Get().NewReference(sDomain);
|
||||
}
|
||||
CTranslationDomainRefHolder::~CTranslationDomainRefHolder() {
|
||||
CTranslation::Get().DelReference(m_sDomain);
|
||||
}
|
||||
@@ -272,6 +272,9 @@ bool CUser::ParseConfig(CConfig* pConfig, CString& sError) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pConfig->FindStringEntry("language", sValue)) {
|
||||
SetLanguage(sValue);
|
||||
}
|
||||
pConfig->FindStringEntry("pass", sValue);
|
||||
// There are different formats for this available:
|
||||
// Pass = <plain text>
|
||||
@@ -749,6 +752,7 @@ bool CUser::Clone(const CUser& User, CString& sErrorRet, bool bCloneNetworks) {
|
||||
SetMaxQueryBuffers(User.MaxQueryBuffers());
|
||||
SetMaxJoins(User.MaxJoins());
|
||||
SetClientEncoding(User.GetClientEncoding());
|
||||
SetLanguage(User.GetLanguage());
|
||||
|
||||
// Allowed Hosts
|
||||
m_ssAllowedHosts.clear();
|
||||
@@ -1057,6 +1061,7 @@ CConfig CUser::ToConfig() const {
|
||||
config.AddKeyValuePair("MaxQueryBuffers", CString(m_uMaxQueryBuffers));
|
||||
config.AddKeyValuePair("MaxJoins", CString(m_uMaxJoins));
|
||||
config.AddKeyValuePair("ClientEncoding", GetClientEncoding());
|
||||
config.AddKeyValuePair("Language", GetLanguage());
|
||||
|
||||
// Allow Hosts
|
||||
if (!m_ssAllowedHosts.empty()) {
|
||||
@@ -1398,6 +1403,18 @@ bool CUser::SetStatusPrefix(const CString& s) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CUser::SetLanguage(const CString& s) {
|
||||
// They look like ru_RU
|
||||
for (char c : s) {
|
||||
if (isalpha(c) || c == '_') {
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
m_sLanguage = s;
|
||||
return true;
|
||||
}
|
||||
// !Setters
|
||||
|
||||
// Getters
|
||||
@@ -1462,6 +1479,7 @@ bool CUser::AutoClearQueryBuffer() const { return m_bAutoClearQueryBuffer; }
|
||||
// CString CUser::GetSkinName() const { return (!m_sSkinName.empty()) ?
|
||||
// m_sSkinName : CZNC::Get().GetSkinName(); }
|
||||
CString CUser::GetSkinName() const { return m_sSkinName; }
|
||||
CString CUser::GetLanguage() const { return m_sLanguage; }
|
||||
const CString& CUser::GetUserPath() const {
|
||||
if (!CFile::Exists(m_sUserPath)) {
|
||||
CDir::MakeDir(m_sUserPath);
|
||||
|
||||
@@ -672,6 +672,8 @@ CWebSock::EPageReqResult CWebSock::OnPageRequestInternal(const CString& sURI,
|
||||
m_sUser = GetSession()->GetUser()->GetUserName();
|
||||
m_bLoggedIn = true;
|
||||
}
|
||||
CLanguageScope user_language(
|
||||
m_bLoggedIn ? GetSession()->GetUser()->GetLanguage() : "");
|
||||
|
||||
// Handle the static pages that don't require a login
|
||||
if (sURI == "/") {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#
|
||||
# Copyright (C) 2004-2016 ZNC, see the NOTICE file for details.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
include(translation)
|
||||
|
||||
set(tmpl_dirs)
|
||||
file(GLOB skins "${PROJECT_SOURCE_DIR}/webskins/*")
|
||||
foreach(skin ${skins})
|
||||
list(APPEND tmpl_dirs "${skin}/tmpl")
|
||||
endforeach()
|
||||
translation(SHORT "znc" FULL "znc" SOURCES ${znc_cpp}
|
||||
TMPLDIRS ${tmpl_dirs})
|
||||
+12
-4
@@ -75,7 +75,8 @@ CZNC::CZNC()
|
||||
m_uiForceEncoding(0),
|
||||
m_sConnectThrottle(),
|
||||
m_bProtectWebSessions(true),
|
||||
m_bHideVersion(false) {
|
||||
m_bHideVersion(false),
|
||||
m_Translation("znc") {
|
||||
if (!InitCsocket()) {
|
||||
CUtils::PrintError("Could not initialize Csocket!");
|
||||
exit(-1);
|
||||
@@ -160,11 +161,17 @@ CString CZNC::GetCompileOptionsString() {
|
||||
#else
|
||||
"no"
|
||||
#endif
|
||||
", build: "
|
||||
", build: "
|
||||
#ifdef BUILD_WITH_CMAKE
|
||||
"cmake"
|
||||
"cmake"
|
||||
#else
|
||||
"autoconf"
|
||||
"autoconf"
|
||||
#endif
|
||||
", i18n: "
|
||||
#ifdef HAVE_I18N
|
||||
"yes"
|
||||
#else
|
||||
"no"
|
||||
#endif
|
||||
;
|
||||
}
|
||||
@@ -1419,6 +1426,7 @@ void CZNC::Broadcast(const CString& sMessage, bool bAdminOnly, CUser* pSkipUser,
|
||||
if (bAdminOnly && !it.second->IsAdmin()) continue;
|
||||
|
||||
if (it.second != pSkipUser) {
|
||||
// TODO: translate message to user's language
|
||||
CString sMsg = sMessage;
|
||||
|
||||
bool bContinue = false;
|
||||
|
||||
Reference in New Issue
Block a user