Add framework for translating ZNC to different languages

This commit is contained in:
Alexey Sokolov
2016-01-21 08:19:20 +00:00
parent 10785ee90e
commit 8eeeaf71a0
26 changed files with 797 additions and 54 deletions
+45 -17
View File
@@ -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
View File
@@ -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*
+91
View File
@@ -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
+3
View File
@@ -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;
+34
View File
@@ -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
+2
View File
@@ -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
+2
View File
@@ -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 */