mirror of
https://github.com/znc/znc.git
synced 2026-07-02 07:51:26 +02:00
Add framework for translating ZNC to different languages
This commit is contained in:
+45
-17
@@ -23,6 +23,7 @@
|
||||
#include <znc/Threads.h>
|
||||
#include <znc/Message.h>
|
||||
#include <znc/main.h>
|
||||
#include <znc/Translation.h>
|
||||
#include <functional>
|
||||
#include <set>
|
||||
#include <queue>
|
||||
@@ -58,20 +59,24 @@ class CModInfo;
|
||||
#define ZNC_EXPORT_LIB_EXPORT
|
||||
#endif
|
||||
|
||||
#define MODCOMMONDEFS(CLASS, DESCRIPTION, TYPE) \
|
||||
extern "C" { \
|
||||
ZNC_EXPORT_LIB_EXPORT bool ZNCModInfo(double dCoreVersion, \
|
||||
CModInfo& Info); \
|
||||
ZNC_EXPORT_LIB_EXPORT bool ZNCModInfo(double dCoreVersion, \
|
||||
CModInfo& Info) { \
|
||||
if (dCoreVersion != VERSION) return false; \
|
||||
Info.SetDescription(DESCRIPTION); \
|
||||
Info.SetDefaultType(TYPE); \
|
||||
Info.AddType(TYPE); \
|
||||
Info.SetLoader(TModLoad<CLASS>); \
|
||||
TModInfo<CLASS>(Info); \
|
||||
return true; \
|
||||
} \
|
||||
#define MODCOMMONDEFS(CLASS, DESCRIPTION, TYPE) \
|
||||
extern "C" { \
|
||||
ZNC_EXPORT_LIB_EXPORT bool ZNCModInfo(double dCoreVersion, \
|
||||
CModInfo& Info); \
|
||||
ZNC_EXPORT_LIB_EXPORT bool ZNCModInfo(double dCoreVersion, \
|
||||
CModInfo& Info) { \
|
||||
if (dCoreVersion != VERSION) return false; \
|
||||
auto t = [&](const CString& sEnglish, const CString& sContext = "") { \
|
||||
return sEnglish.empty() ? "" : Info.t(sEnglish, sContext); \
|
||||
}; \
|
||||
t(CString()); /* Don't warn about unused t */ \
|
||||
Info.SetDescription(DESCRIPTION); \
|
||||
Info.SetDefaultType(TYPE); \
|
||||
Info.AddType(TYPE); \
|
||||
Info.SetLoader(TModLoad<CLASS>); \
|
||||
TModInfo<CLASS>(Info); \
|
||||
return true; \
|
||||
} \
|
||||
}
|
||||
|
||||
/** Instead of writing a constructor, you should call this macro. It accepts all
|
||||
@@ -273,6 +278,9 @@ class CModInfo {
|
||||
void SetLoader(ModLoader fLoader) { m_fLoader = fLoader; }
|
||||
void SetDefaultType(EModuleType eType) { m_eDefaultType = eType; }
|
||||
// !Setters
|
||||
|
||||
CString t(const CString& sEnglish, const CString& sContext = "") const;
|
||||
|
||||
private:
|
||||
protected:
|
||||
std::set<EModuleType> m_seType;
|
||||
@@ -316,16 +324,18 @@ class CModCommand {
|
||||
const CString& sArgs, const CString& sDesc);
|
||||
CModCommand(const CString& sCmd, CmdFunc func, const CString& sArgs,
|
||||
const CString& sDesc);
|
||||
CModCommand(const CString& sCmd, CmdFunc func, const CString& sArgs,
|
||||
const CDelayedTranslation& dDesc);
|
||||
|
||||
/** Copy constructor, needed so that this can be saved in a std::map.
|
||||
* @param other Object to copy from.
|
||||
*/
|
||||
CModCommand(const CModCommand& other);
|
||||
CModCommand(const CModCommand& other) = default;
|
||||
|
||||
/** Assignment operator, needed so that this can be saved in a std::map.
|
||||
* @param other Object to copy from.
|
||||
*/
|
||||
CModCommand& operator=(const CModCommand& other);
|
||||
CModCommand& operator=(const CModCommand& other) = default;
|
||||
|
||||
/** Initialize a CTable so that it can be used with AddHelp().
|
||||
* @param Table The instance of CTable to initialize.
|
||||
@@ -341,7 +351,7 @@ class CModCommand {
|
||||
const CString& GetCommand() const { return m_sCmd; }
|
||||
CmdFunc GetFunction() const { return m_pFunc; }
|
||||
const CString& GetArgs() const { return m_sArgs; }
|
||||
const CString& GetDescription() const { return m_sDesc; }
|
||||
CString GetDescription() const;
|
||||
|
||||
void Call(const CString& sLine) const { m_pFunc(sLine); }
|
||||
|
||||
@@ -350,6 +360,8 @@ class CModCommand {
|
||||
CmdFunc m_pFunc;
|
||||
CString m_sArgs;
|
||||
CString m_sDesc;
|
||||
CDelayedTranslation m_dDesc;
|
||||
bool m_bTranslating;
|
||||
};
|
||||
|
||||
/** The base class for your own ZNC modules.
|
||||
@@ -1093,6 +1105,10 @@ class CModule {
|
||||
bool AddCommand(const CString& sCmd, const CString& sArgs,
|
||||
const CString& sDesc,
|
||||
std::function<void(const CString& sLine)> func);
|
||||
/// @return True if the command was successfully added.
|
||||
bool AddCommand(const CString& sCmd, const CString& sArgs,
|
||||
const CDelayedTranslation& dDesc,
|
||||
std::function<void(const CString& sLine)> func);
|
||||
/// @return True if the command was successfully removed.
|
||||
bool RemCommand(const CString& sCmd);
|
||||
/// @return The CModCommand instance or nullptr if none was found.
|
||||
@@ -1267,6 +1283,17 @@ class CModule {
|
||||
CModInfo::EModuleType eType);
|
||||
// !Global Modules
|
||||
|
||||
#ifndef SWIG
|
||||
// Translation
|
||||
CString t(const CString& sEnglish, const CString& sContext = "") const;
|
||||
CInlineFormatMessage f(const CString& sEnglish,
|
||||
const CString& sContext = "") const;
|
||||
CInlineFormatMessage p(const CString& sEnglish, const CString& sEnglishes,
|
||||
int iNum, const CString& sContext = "") const;
|
||||
CDelayedTranslation d(const CString& sEnglish,
|
||||
const CString& sContext = "") const;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
CModInfo::EModuleType m_eType;
|
||||
CString m_sDescription;
|
||||
@@ -1285,6 +1312,7 @@ class CModule {
|
||||
CString m_sSavePath;
|
||||
CString m_sArgs;
|
||||
CString m_sModPath;
|
||||
CTranslationDomainRefHolder m_Translation;
|
||||
|
||||
private:
|
||||
MCString
|
||||
|
||||
+14
-1
@@ -20,10 +20,11 @@
|
||||
#include <znc/zncconfig.h>
|
||||
#include <znc/Csocket.h>
|
||||
#include <znc/Threads.h>
|
||||
#include <znc/Translation.h>
|
||||
|
||||
class CModule;
|
||||
|
||||
class CZNCSock : public Csock {
|
||||
class CZNCSock : public Csock, public CCoreTranslationMixin {
|
||||
public:
|
||||
CZNCSock(int timeout = 60);
|
||||
CZNCSock(const CString& sHost, u_short port, int timeout = 60);
|
||||
@@ -267,6 +268,18 @@ class CSocket : public CZNCSock {
|
||||
// Getters
|
||||
CModule* GetModule() const;
|
||||
// !Getters
|
||||
|
||||
#ifndef SWIG
|
||||
// Translation. As opposed to CCoreTranslationMixin, this one uses module.mo
|
||||
CString t(const CString& sEnglish, const CString& sContext = "") const;
|
||||
CInlineFormatMessage f(const CString& sEnglish,
|
||||
const CString& sContext = "") const;
|
||||
CInlineFormatMessage p(const CString& sEnglish, const CString& sEnglishes,
|
||||
int iNum, const CString& sContext) const;
|
||||
CDelayedTranslation d(const CString& sEnglish,
|
||||
const CString& sContext = "") const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
protected:
|
||||
CModule*
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef ZNC_TRANSLATION_H
|
||||
#define ZNC_TRANSLATION_H
|
||||
|
||||
#include <znc/ZNCString.h>
|
||||
#include <unordered_map>
|
||||
|
||||
// All instances of modules share single message map using this class stored in
|
||||
// CZNC.
|
||||
class CTranslation {
|
||||
public:
|
||||
static CTranslation& Get();
|
||||
CString Singular(const CString& sDomain, const CString& sContext,
|
||||
const CString& sEnglish);
|
||||
CString Plural(const CString& sDomain, const CString& sContext,
|
||||
const CString& sEnglish, const CString& sEnglishes,
|
||||
int iNum);
|
||||
|
||||
void PushLanguage(const CString& sLanguage);
|
||||
void PopLanguage();
|
||||
|
||||
void NewReference(const CString& sDomain);
|
||||
void DelReference(const CString& sDomain);
|
||||
|
||||
private:
|
||||
const std::locale& LoadTranslation(const CString& sDomain);
|
||||
std::unordered_map<CString /* domain */,
|
||||
std::unordered_map<CString /* language */, std::locale>>
|
||||
m_Translations;
|
||||
VCString m_sLanguageStack;
|
||||
std::unordered_map<CString /* domain */, int> m_miReferences;
|
||||
};
|
||||
|
||||
struct CLanguageScope {
|
||||
explicit CLanguageScope(const CString& sLanguage);
|
||||
~CLanguageScope();
|
||||
};
|
||||
|
||||
struct CTranslationDomainRefHolder {
|
||||
explicit CTranslationDomainRefHolder(const CString& sDomain);
|
||||
~CTranslationDomainRefHolder();
|
||||
|
||||
private:
|
||||
CString m_sDomain;
|
||||
};
|
||||
|
||||
// This is inspired by boost::locale::message, but without boost
|
||||
class CDelayedTranslation {
|
||||
public:
|
||||
CDelayedTranslation() = default;
|
||||
CDelayedTranslation(const CString& sDomain, const CString& sContext,
|
||||
const CString& sEnglish)
|
||||
: m_sDomain(sDomain), m_sContext(sContext), m_sEnglish(sEnglish) {}
|
||||
CString Resolve() const;
|
||||
|
||||
private:
|
||||
CString m_sDomain;
|
||||
CString m_sContext;
|
||||
CString m_sEnglish;
|
||||
};
|
||||
|
||||
// Used by everything except modules.
|
||||
// CModule defines its own version of these functions.
|
||||
class CCoreTranslationMixin {
|
||||
protected:
|
||||
static CString t(const CString& sEnglish, const CString& sContext = "");
|
||||
static CInlineFormatMessage f(const CString& sEnglish,
|
||||
const CString& sContext = "");
|
||||
static CInlineFormatMessage p(const CString& sEnglish,
|
||||
const CString& sEnglishes, int iNum,
|
||||
const CString& sContext = "");
|
||||
static CDelayedTranslation d(const CString& sEnglish,
|
||||
const CString& sContext = "");
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -143,6 +143,7 @@ class CUser {
|
||||
bool SetQueryBufferSize(unsigned int u, bool bForce = false);
|
||||
void SetAutoClearChanBuffer(bool b);
|
||||
void SetAutoClearQueryBuffer(bool b);
|
||||
bool SetLanguage(const CString& s);
|
||||
|
||||
void SetBeingDeleted(bool b) { m_bBeingDeleted = b; }
|
||||
void SetTimestampFormat(const CString& s) { m_sTimestampFormat = s; }
|
||||
@@ -200,6 +201,7 @@ class CUser {
|
||||
unsigned int JoinTries() const { return m_uMaxJoinTries; }
|
||||
unsigned int MaxJoins() const { return m_uMaxJoins; }
|
||||
CString GetSkinName() const;
|
||||
CString GetLanguage() const;
|
||||
unsigned int MaxNetworks() const { return m_uMaxNetworks; }
|
||||
unsigned int MaxQueryBuffers() const { return m_uMaxQueryBuffers; }
|
||||
// !Getters
|
||||
@@ -253,6 +255,7 @@ class CUser {
|
||||
unsigned int m_uMaxQueryBuffers;
|
||||
unsigned int m_uMaxJoins;
|
||||
CString m_sSkinName;
|
||||
CString m_sLanguage;
|
||||
|
||||
CModules* m_pModules;
|
||||
|
||||
|
||||
@@ -663,4 +663,38 @@ class MCString : public std::map<CString, CString> {
|
||||
virtual CString& Decode(CString& sValue) const;
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<CString> : hash<std::string> {};
|
||||
}
|
||||
|
||||
// Make translateable messages easy to write:
|
||||
// _f("Foo is {1}")(foo)
|
||||
class CInlineFormatMessage {
|
||||
public:
|
||||
explicit CInlineFormatMessage(const CString& sFormat)
|
||||
: m_sFormat(sFormat) {}
|
||||
explicit CInlineFormatMessage(CString&& sFormat)
|
||||
: m_sFormat(std::move(sFormat)) {}
|
||||
|
||||
template <typename... Args>
|
||||
CString operator()(const Args&... args) const {
|
||||
MCString values;
|
||||
apply(values, 1, args...);
|
||||
return CString::NamedFormat(m_sFormat, values);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Arg, typename... Rest>
|
||||
void apply(MCString& values, int index, const Arg& arg,
|
||||
const Rest&... rest) const {
|
||||
values[CString(index)] = CString(arg);
|
||||
apply(values, index + 1, rest...);
|
||||
}
|
||||
|
||||
void apply(MCString& values, int index) const {}
|
||||
|
||||
CString m_sFormat;
|
||||
};
|
||||
|
||||
#endif // !ZNCSTRING_H
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <znc/Modules.h>
|
||||
#include <znc/Socket.h>
|
||||
#include <znc/Listener.h>
|
||||
#include <znc/Translation.h>
|
||||
#include <mutex>
|
||||
#include <map>
|
||||
#include <list>
|
||||
@@ -298,6 +299,7 @@ class CZNC {
|
||||
TCacheMap<CString> m_sConnectThrottle;
|
||||
bool m_bProtectWebSessions;
|
||||
bool m_bHideVersion;
|
||||
CTranslationDomainRefHolder m_Translation;
|
||||
};
|
||||
|
||||
#endif // !ZNC_H
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#cmakedefine HAVE_LIBSSL 1
|
||||
#cmakedefine HAVE_IPV6 1
|
||||
#cmakedefine HAVE_ZLIB 1
|
||||
#cmakedefine HAVE_I18N 1
|
||||
#cmakedefine CSOCK_USE_POLL 1
|
||||
|
||||
#cmakedefine HAVE_GETOPT_LONG 1
|
||||
@@ -49,5 +50,6 @@
|
||||
|
||||
#define _MODDIR_ "@CMAKE_INSTALL_FULL_LIBDIR@/znc"
|
||||
#define _DATADIR_ "@CMAKE_INSTALL_FULL_DATADIR@/znc"
|
||||
#define LOCALE_DIR "@CMAKE_INSTALL_FULL_LOCALEDIR@"
|
||||
|
||||
#endif /* ZNCCONFIG_H */
|
||||
|
||||
Reference in New Issue
Block a user