#include "myintl.h"
#include "tools.h"
#include "wx/log.h"
#include "wx/hashmap.h"
#include "wx/utils.h"
#include "wx/ptr_scpd.h"
#include "wx/fontmap.h"
#include "wx/stdpaths.h"
#include "wx/file.h"
#include "wx/filename.h"
#include <memory>
#include <sstream>
typedef wxUint8 size_t8;
typedef wxUint32 size_t32;
const size_t32 MSGCATALOG_MAGIC = 0x950412de;
const size_t32 MSGCATALOG_MAGIC_SW = 0xde120495;
class MyPluralFormsToken
{
public:
enum Type
{
T_ERROR, T_EOF, T_NUMBER, T_N, T_PLURAL, T_NPLURALS, T_EQUAL, T_ASSIGN,
T_GREATER, T_GREATER_OR_EQUAL, T_LESS, T_LESS_OR_EQUAL,
T_REMINDER, T_NOT_EQUAL,
T_LOGICAL_AND, T_LOGICAL_OR, T_QUESTION, T_COLON, T_SEMICOLON,
T_LEFT_BRACKET, T_RIGHT_BRACKET
};
Type type() const { return m_type; }
void setType(Type type) { m_type = type; }
typedef int Number;
Number number() const { return m_number; }
void setNumber(Number num) { m_number = num; }
private:
Type m_type;
Number m_number;
};
class MyPluralFormsScanner
{
public:
MyPluralFormsScanner(const char* s);
const MyPluralFormsToken& token() const { return m_token; }
bool nextToken();
private:
const char* m_s;
MyPluralFormsToken m_token;
};
MyPluralFormsScanner::MyPluralFormsScanner(const char* s) : m_s(s)
{
nextToken();
}
bool MyPluralFormsScanner::nextToken()
{
MyPluralFormsToken::Type type = MyPluralFormsToken::T_ERROR;
while (isspace(*m_s))
{
++m_s;
}
if (*m_s == 0)
{
type = MyPluralFormsToken::T_EOF;
}
else if (isdigit(*m_s))
{
MyPluralFormsToken::Number number = *m_s++ - '0';
while (isdigit(*m_s))
{
number = number * 10 + (*m_s++ - '0');
}
m_token.setNumber(number);
type = MyPluralFormsToken::T_NUMBER;
}
else if (isalpha(*m_s))
{
const char* begin = m_s++;
while (isalnum(*m_s))
{
++m_s;
}
size_t size = m_s - begin;
if (size == 1 && memcmp(begin, "n", size) == 0)
{
type = MyPluralFormsToken::T_N;
}
else if (size == 6 && memcmp(begin, "plural", size) == 0)
{
type = MyPluralFormsToken::T_PLURAL;
}
else if (size == 8 && memcmp(begin, "nplurals", size) == 0)
{
type = MyPluralFormsToken::T_NPLURALS;
}
}
else if (*m_s == '=')
{
++m_s;
if (*m_s == '=')
{
++m_s;
type = MyPluralFormsToken::T_EQUAL;
}
else
{
type = MyPluralFormsToken::T_ASSIGN;
}
}
else if (*m_s == '>')
{
++m_s;
if (*m_s == '=')
{
++m_s;
type = MyPluralFormsToken::T_GREATER_OR_EQUAL;
}
else
{
type = MyPluralFormsToken::T_GREATER;
}
}
else if (*m_s == '<')
{
++m_s;
if (*m_s == '=')
{
++m_s;
type = MyPluralFormsToken::T_LESS_OR_EQUAL;
}
else
{
type = MyPluralFormsToken::T_LESS;
}
}
else if (*m_s == '%')
{
++m_s;
type = MyPluralFormsToken::T_REMINDER;
}
else if (*m_s == '!' && m_s[1] == '=')
{
m_s += 2;
type = MyPluralFormsToken::T_NOT_EQUAL;
}
else if (*m_s == '&' && m_s[1] == '&')
{
m_s += 2;
type = MyPluralFormsToken::T_LOGICAL_AND;
}
else if (*m_s == '|' && m_s[1] == '|')
{
m_s += 2;
type = MyPluralFormsToken::T_LOGICAL_OR;
}
else if (*m_s == '?')
{
++m_s;
type = MyPluralFormsToken::T_QUESTION;
}
else if (*m_s == ':')
{
++m_s;
type = MyPluralFormsToken::T_COLON;
} else if (*m_s == ';') {
++m_s;
type = MyPluralFormsToken::T_SEMICOLON;
}
else if (*m_s == '(')
{
++m_s;
type = MyPluralFormsToken::T_LEFT_BRACKET;
}
else if (*m_s == ')')
{
++m_s;
type = MyPluralFormsToken::T_RIGHT_BRACKET;
}
m_token.setType(type);
return type != MyPluralFormsToken::T_ERROR;
}
class MyPluralFormsNode;
class MyPluralFormsNodePtr
{
public:
MyPluralFormsNodePtr(MyPluralFormsNode *p = NULL) : m_p(p) {}
~MyPluralFormsNodePtr();
MyPluralFormsNode& operator*() const { return *m_p; }
MyPluralFormsNode* operator->() const { return m_p; }
MyPluralFormsNode* get() const { return m_p; }
MyPluralFormsNode* release();
void reset(MyPluralFormsNode *p);
private:
MyPluralFormsNode *m_p;
};
class MyPluralFormsNode
{
public:
MyPluralFormsNode(const MyPluralFormsToken& token) : m_token(token) {}
const MyPluralFormsToken& token() const { return m_token; }
const MyPluralFormsNode* node(size_t i) const { return m_nodes[i].get(); }
void setNode(size_t i, MyPluralFormsNode* n);
MyPluralFormsNode* releaseNode(size_t i);
MyPluralFormsToken::Number evaluate(MyPluralFormsToken::Number n) const;
private:
MyPluralFormsToken m_token;
MyPluralFormsNodePtr m_nodes[3];
};
MyPluralFormsNodePtr::~MyPluralFormsNodePtr()
{
delete m_p;
}
MyPluralFormsNode* MyPluralFormsNodePtr::release()
{
MyPluralFormsNode *p = m_p;
m_p = NULL;
return p;
}
void MyPluralFormsNodePtr::reset(MyPluralFormsNode *p)
{
if (p != m_p)
{
delete m_p;
m_p = p;
}
}
void MyPluralFormsNode::setNode(size_t i, MyPluralFormsNode* n)
{
m_nodes[i].reset(n);
}
MyPluralFormsNode* MyPluralFormsNode::releaseNode(size_t i)
{
return m_nodes[i].release();
}
MyPluralFormsToken::Number
MyPluralFormsNode::evaluate(MyPluralFormsToken::Number n) const
{
switch (token().type())
{
case MyPluralFormsToken::T_NUMBER:
return token().number();
case MyPluralFormsToken::T_N:
return n;
case MyPluralFormsToken::T_EQUAL:
return node(0)->evaluate(n) == node(1)->evaluate(n);
case MyPluralFormsToken::T_NOT_EQUAL:
return node(0)->evaluate(n) != node(1)->evaluate(n);
case MyPluralFormsToken::T_GREATER:
return node(0)->evaluate(n) > node(1)->evaluate(n);
case MyPluralFormsToken::T_GREATER_OR_EQUAL:
return node(0)->evaluate(n) >= node(1)->evaluate(n);
case MyPluralFormsToken::T_LESS:
return node(0)->evaluate(n) < node(1)->evaluate(n);
case MyPluralFormsToken::T_LESS_OR_EQUAL:
return node(0)->evaluate(n) <= node(1)->evaluate(n);
case MyPluralFormsToken::T_REMINDER:
{
MyPluralFormsToken::Number number = node(1)->evaluate(n);
if (number != 0)
{
return node(0)->evaluate(n) % number;
}
else
{
return 0;
}
}
case MyPluralFormsToken::T_LOGICAL_AND:
return node(0)->evaluate(n) && node(1)->evaluate(n);
case MyPluralFormsToken::T_LOGICAL_OR:
return node(0)->evaluate(n) || node(1)->evaluate(n);
case MyPluralFormsToken::T_QUESTION:
return node(0)->evaluate(n)
? node(1)->evaluate(n)
: node(2)->evaluate(n);
default:
return 0;
}
}
class MyPluralFormsCalculator
{
public:
MyPluralFormsCalculator() : m_nplurals(0), m_plural(0) {}
int evaluate(int n) const;
static MyPluralFormsCalculator* make(const char* s = 0);
~MyPluralFormsCalculator() {}
void init(MyPluralFormsToken::Number nplurals, MyPluralFormsNode* plural);
private:
MyPluralFormsToken::Number m_nplurals;
MyPluralFormsNodePtr m_plural;
};
wxDEFINE_SCOPED_PTR_TYPE(MyPluralFormsCalculator)
void MyPluralFormsCalculator::init(MyPluralFormsToken::Number nplurals,
MyPluralFormsNode* plural)
{
m_nplurals = nplurals;
m_plural.reset(plural);
}
int MyPluralFormsCalculator::evaluate(int n) const
{
if (m_plural.get() == 0)
{
return 0;
}
MyPluralFormsToken::Number number = m_plural->evaluate(n);
if (number < 0 || number > m_nplurals)
{
return 0;
}
return number;
}
class MyPluralFormsParser
{
public:
MyPluralFormsParser(MyPluralFormsScanner& scanner) : m_scanner(scanner) {}
bool parse(MyPluralFormsCalculator& rCalculator);
private:
MyPluralFormsNode* parsePlural();
MyPluralFormsScanner& m_scanner;
const MyPluralFormsToken& token() const;
bool nextToken();
MyPluralFormsNode* expression();
MyPluralFormsNode* logicalOrExpression();
MyPluralFormsNode* logicalAndExpression();
MyPluralFormsNode* equalityExpression();
MyPluralFormsNode* multiplicativeExpression();
MyPluralFormsNode* relationalExpression();
MyPluralFormsNode* pmExpression();
};
bool MyPluralFormsParser::parse(MyPluralFormsCalculator& rCalculator)
{
if (token().type() != MyPluralFormsToken::T_NPLURALS)
return false;
if (!nextToken())
return false;
if (token().type() != MyPluralFormsToken::T_ASSIGN)
return false;
if (!nextToken())
return false;
if (token().type() != MyPluralFormsToken::T_NUMBER)
return false;
MyPluralFormsToken::Number nplurals = token().number();
if (!nextToken())
return false;
if (token().type() != MyPluralFormsToken::T_SEMICOLON)
return false;
if (!nextToken())
return false;
if (token().type() != MyPluralFormsToken::T_PLURAL)
return false;
if (!nextToken())
return false;
if (token().type() != MyPluralFormsToken::T_ASSIGN)
return false;
if (!nextToken())
return false;
MyPluralFormsNode* plural = parsePlural();
if (plural == 0)
return false;
if (token().type() != MyPluralFormsToken::T_SEMICOLON)
return false;
if (!nextToken())
return false;
if (token().type() != MyPluralFormsToken::T_EOF)
return false;
rCalculator.init(nplurals, plural);
return true;
}
MyPluralFormsNode* MyPluralFormsParser::parsePlural()
{
MyPluralFormsNode* p = expression();
if (p == NULL)
{
return NULL;
}
MyPluralFormsNodePtr n(p);
if (token().type() != MyPluralFormsToken::T_SEMICOLON)
{
return NULL;
}
return n.release();
}
const MyPluralFormsToken& MyPluralFormsParser::token() const
{
return m_scanner.token();
}
bool MyPluralFormsParser::nextToken()
{
if (!m_scanner.nextToken())
return false;
return true;
}
MyPluralFormsNode* MyPluralFormsParser::expression()
{
MyPluralFormsNode* p = logicalOrExpression();
if (p == NULL)
return NULL;
MyPluralFormsNodePtr n(p);
if (token().type() == MyPluralFormsToken::T_QUESTION)
{
MyPluralFormsNodePtr qn(new MyPluralFormsNode(token()));
if (!nextToken())
{
return 0;
}
p = expression();
if (p == 0)
{
return 0;
}
qn->setNode(1, p);
if (token().type() != MyPluralFormsToken::T_COLON)
{
return 0;
}
if (!nextToken())
{
return 0;
}
p = expression();
if (p == 0)
{
return 0;
}
qn->setNode(2, p);
qn->setNode(0, n.release());
return qn.release();
}
return n.release();
}
MyPluralFormsNode*MyPluralFormsParser::logicalOrExpression()
{
MyPluralFormsNode* p = logicalAndExpression();
if (p == NULL)
return NULL;
MyPluralFormsNodePtr ln(p);
if (token().type() == MyPluralFormsToken::T_LOGICAL_OR)
{
MyPluralFormsNodePtr un(new MyPluralFormsNode(token()));
if (!nextToken())
{
return 0;
}
p = logicalOrExpression();
if (p == 0)
{
return 0;
}
MyPluralFormsNodePtr rn(p);
if (rn->token().type() == MyPluralFormsToken::T_LOGICAL_OR)
{
un->setNode(0, ln.release());
un->setNode(1, rn->releaseNode(0));
rn->setNode(0, un.release());
return rn.release();
}
un->setNode(0, ln.release());
un->setNode(1, rn.release());
return un.release();
}
return ln.release();
}
MyPluralFormsNode* MyPluralFormsParser::logicalAndExpression()
{
MyPluralFormsNode* p = equalityExpression();
if (p == NULL)
return NULL;
MyPluralFormsNodePtr ln(p);
if (token().type() == MyPluralFormsToken::T_LOGICAL_AND)
{
MyPluralFormsNodePtr un(new MyPluralFormsNode(token()));
if (!nextToken())
{
return NULL;
}
p = logicalAndExpression();
if (p == 0)
{
return NULL;
}
MyPluralFormsNodePtr rn(p);
if (rn->token().type() == MyPluralFormsToken::T_LOGICAL_AND)
{
un->setNode(0, ln.release());
un->setNode(1, rn->releaseNode(0));
rn->setNode(0, un.release());
return rn.release();
}
un->setNode(0, ln.release());
un->setNode(1, rn.release());
return un.release();
}
return ln.release();
}
MyPluralFormsNode* MyPluralFormsParser::equalityExpression()
{
MyPluralFormsNode* p = relationalExpression();
if (p == NULL)
return NULL;
MyPluralFormsNodePtr n(p);
if (token().type() == MyPluralFormsToken::T_EQUAL ||
token().type() == MyPluralFormsToken::T_NOT_EQUAL)
{
MyPluralFormsNodePtr qn(new MyPluralFormsNode(token()));
if (!nextToken())
{
return NULL;
}
p = relationalExpression();
if (p == NULL)
{
return NULL;
}
qn->setNode(1, p);
qn->setNode(0, n.release());
return qn.release();
}
return n.release();
}
MyPluralFormsNode* MyPluralFormsParser::relationalExpression()
{
MyPluralFormsNode* p = multiplicativeExpression();
if (p == NULL)
return NULL;
MyPluralFormsNodePtr n(p);
if (token().type() == MyPluralFormsToken::T_GREATER ||
token().type() == MyPluralFormsToken::T_LESS ||
token().type() == MyPluralFormsToken::T_GREATER_OR_EQUAL ||
token().type() == MyPluralFormsToken::T_LESS_OR_EQUAL)
{
MyPluralFormsNodePtr qn(new MyPluralFormsNode(token()));
if (!nextToken())
{
return NULL;
}
p = multiplicativeExpression();
if (p == NULL)
{
return NULL;
}
qn->setNode(1, p);
qn->setNode(0, n.release());
return qn.release();
}
return n.release();
}
MyPluralFormsNode* MyPluralFormsParser::multiplicativeExpression()
{
MyPluralFormsNode* p = pmExpression();
if (p == NULL)
return NULL;
MyPluralFormsNodePtr n(p);
if (token().type() == MyPluralFormsToken::T_REMINDER)
{
MyPluralFormsNodePtr qn(new MyPluralFormsNode(token()));
if (!nextToken())
{
return NULL;
}
p = pmExpression();
if (p == NULL)
{
return NULL;
}
qn->setNode(1, p);
qn->setNode(0, n.release());
return qn.release();
}
return n.release();
}
MyPluralFormsNode* MyPluralFormsParser::pmExpression()
{
MyPluralFormsNodePtr n;
if (token().type() == MyPluralFormsToken::T_N
|| token().type() == MyPluralFormsToken::T_NUMBER)
{
n.reset(new MyPluralFormsNode(token()));
if (!nextToken())
{
return NULL;
}
}
else if (token().type() == MyPluralFormsToken::T_LEFT_BRACKET) {
if (!nextToken())
{
return NULL;
}
MyPluralFormsNode* p = expression();
if (p == NULL)
{
return NULL;
}
n.reset(p);
if (token().type() != MyPluralFormsToken::T_RIGHT_BRACKET)
{
return NULL;
}
if (!nextToken())
{
return NULL;
}
}
else
{
return NULL;
}
return n.release();
}
MyPluralFormsCalculator* MyPluralFormsCalculator::make(const char* s)
{
MyPluralFormsCalculatorPtr calculator(new MyPluralFormsCalculator);
if (s != NULL)
{
MyPluralFormsScanner scanner(s);
MyPluralFormsParser p(scanner);
if (!p.parse(*calculator))
{
return NULL;
}
}
return calculator.release();
}
WX_DECLARE_STRING_HASH_MAP(wxString, MyMessagesHash);
class MyMsgCatalogMemory
{
public:
MyMsgCatalogMemory();
~MyMsgCatalogMemory();
bool Load(const char* pData, size_t32 nDataLen, size_t32 nCatalogUncomp,
MyPluralFormsCalculatorPtr& rPluralFormsCalculator);
bool LoadFile(const wxChar *szDirPrefix, const wxChar *szName,
MyPluralFormsCalculatorPtr& rPluralFormsCalculator);
void FillHash(MyMessagesHash& hash,
const wxString& msgIdCharset,
bool convertEncoding) const;
wxString GetCharset() const { return m_charset; }
private:
struct MyMsgTableEntry
{
size_t32 nLen;
size_t32 ofsString;
};
struct MyMsgCatalogHeader
{
size_t32 magic,
revision,
numStrings;
size_t32 ofsOrigTable,
ofsTransTable;
size_t32 nHashSize,
ofsHashTable;
};
std::string m_strData;
size_t32 m_numStrings;
const MyMsgTableEntry *m_pOrigTable,
*m_pTransTable;
wxString m_charset;
size_t32 Swap(size_t32 ui) const
{
return m_bSwapped
? (ui << 24) | ((ui & 0xff00) << 8) | ((ui >> 8) & 0xff00) | (ui >> 24)
: ui;
}
const char *StringAtOfs(const MyMsgTableEntry *pTable, size_t32 n) const
{
const MyMsgTableEntry * const ent = pTable + n;
size_t32 ofsString = Swap(ent->ofsString);
if ( ofsString + Swap(ent->nLen) > m_strData.size())
{
return NULL;
}
return m_strData.data() + ofsString;
}
bool m_bSwapped;
DECLARE_NO_COPY_CLASS(MyMsgCatalogMemory);
};
class MyMsgCatalog
{
public:
MyMsgCatalog() { m_conv = NULL; }
~MyMsgCatalog();
bool Load(const wxChar *szName,
const char* pCatalogData, size_t nCatalogDataLen, size_t nCatalogUncomp,
const wxChar *msgIdCharset = NULL, bool bConvertEncoding = false);
bool LoadFile(const wxChar *szDirPrefix, const wxChar *szName,
const wxChar *msgIdCharset, bool bConvertEncoding);
wxString GetName() const { return m_name; }
const wxChar *GetString(const wxChar *sz, size_t n = size_t(-1)) const;
MyMsgCatalog *m_pNext;
private:
MyMessagesHash m_messages;
wxString m_name;
wxCSConv *m_conv;
MyPluralFormsCalculatorPtr m_pluralFormsCalculator;
};
MyMsgCatalogMemory::MyMsgCatalogMemory()
{
}
MyMsgCatalogMemory::~MyMsgCatalogMemory()
{
}
bool MyMsgCatalogMemory::Load(const char* pData, size_t32 nDataLen, size_t32 nCatalogUncomp,
MyPluralFormsCalculatorPtr& rPluralFormsCalculator)
{
if (nCatalogUncomp > 0)
m_strData = decompress(pData, nDataLen, nCatalogUncomp);
else
m_strData = std::string(pData, nDataLen);
bool bValid = m_strData.size() + (size_t)0 > sizeof(MyMsgCatalogHeader);
MyMsgCatalogHeader *pHeader = (MyMsgCatalogHeader *)m_strData.data();
if ( bValid ) {
m_bSwapped = pHeader->magic == MSGCATALOG_MAGIC_SW;
bValid = m_bSwapped || pHeader->magic == MSGCATALOG_MAGIC;
}
if ( !bValid ) {
wxLogWarning(_("Built-in data is not a valid message catalog."));
return false;
}
m_numStrings = Swap(pHeader->numStrings);
m_pOrigTable = (MyMsgTableEntry *)(m_strData.data() +
Swap(pHeader->ofsOrigTable));
m_pTransTable = (MyMsgTableEntry *)(m_strData.data() +
Swap(pHeader->ofsTransTable));
const char* headerData = StringAtOfs(m_pOrigTable, 0);
if (headerData && headerData[0] == 0)
{
wxString header = wxString::FromAscii(StringAtOfs(m_pTransTable, 0));
int begin = header.Find(wxT("Content-Type: text/plain; charset="));
if (begin != wxNOT_FOUND)
{
begin += 34;
size_t end = header.find('\n', begin);
if (end != size_t(-1))
{
m_charset.assign(header, begin, end - begin);
if (m_charset == wxT("CHARSET"))
{
m_charset.Clear();
}
}
}
begin = header.Find(wxT("Plural-Forms:"));
if (begin != wxNOT_FOUND)
{
begin += 13;
size_t end = header.find('\n', begin);
if (end != size_t(-1))
{
wxString pfs(header, begin, end - begin);
MyPluralFormsCalculator* pCalculator = MyPluralFormsCalculator
::make(pfs.ToAscii());
if (pCalculator != 0)
{
rPluralFormsCalculator.reset(pCalculator);
}
else
{
wxLogVerbose(_("Cannot parse Plural-Forms:'%s'"), pfs.c_str());
}
}
}
if (rPluralFormsCalculator.get() == NULL)
{
rPluralFormsCalculator.reset(MyPluralFormsCalculator::make());
}
}
return true;
}
static wxString GetMsgCatalogSubdirs(const wxChar *prefix, const wxChar *lang)
{
wxString pathPrefix;
pathPrefix << prefix << wxFILE_SEP_PATH << lang;
wxString searchPath;
searchPath.reserve(4*pathPrefix.length());
searchPath << pathPrefix << wxFILE_SEP_PATH << wxT("LC_MESSAGES") << wxPATH_SEP
<< prefix << wxFILE_SEP_PATH << wxPATH_SEP
<< pathPrefix;
return searchPath;
}
static wxArrayString gs_searchPrefixes;
static wxString GetFullSearchPath(const wxChar *lang)
{
wxArrayString paths;
paths.reserve(gs_searchPrefixes.size() + 1);
size_t n,
count = gs_searchPrefixes.size();
for ( n = 0; n < count; n++ )
{
paths.Add(GetMsgCatalogSubdirs(gs_searchPrefixes[n], lang));
}
#if wxUSE_STDPATHS
const wxString stdp = wxStandardPaths::Get().
GetLocalizedResourcesDir(lang, wxStandardPaths::ResourceCat_Messages);
if ( paths.Index(stdp) == wxNOT_FOUND )
paths.Add(stdp);
#endif
#ifdef __UNIX__
const wxChar *pszLcPath = wxGetenv(wxT("LC_PATH"));
if ( pszLcPath )
{
const wxString lcp = GetMsgCatalogSubdirs(pszLcPath, lang);
if ( paths.Index(lcp) == wxNOT_FOUND )
paths.Add(lcp);
}
wxString wxp = wxGetInstallPrefix();
if ( !wxp.empty() )
{
wxp = GetMsgCatalogSubdirs(wxp + _T("/share/locale"), lang);
if ( paths.Index(wxp) == wxNOT_FOUND )
paths.Add(wxp);
}
#endif
wxString searchPath;
searchPath.reserve(500);
count = paths.size();
for ( n = 0; n < count; n++ )
{
searchPath += paths[n];
if ( n != count - 1 )
searchPath += wxPATH_SEP;
}
return searchPath;
}
bool MyMsgCatalogMemory::LoadFile(const wxChar *szDirPrefix, const wxChar *szName,
MyPluralFormsCalculatorPtr& rPluralFormsCalculator)
{
wxString searchPath;
#if wxUSE_FONTMAP
wxFontEncoding encSys = wxLocale::GetSystemEncoding();
if ( encSys != wxFONTENCODING_SYSTEM )
{
wxString fullname(szDirPrefix);
fullname << _T('.') << wxFontMapperBase::GetEncodingName(encSys);
searchPath << GetFullSearchPath(fullname) << wxPATH_SEP;
}
#endif
searchPath += GetFullSearchPath(szDirPrefix);
const wxChar *sublocale = wxStrchr(szDirPrefix, wxT('_'));
if ( sublocale )
{
searchPath << wxPATH_SEP
<< GetFullSearchPath(wxString(szDirPrefix).
Left((size_t)(sublocale - szDirPrefix)));
}
wxLogVerbose(_("looking for catalog '%s' in path '%s'."),
szName, searchPath.c_str());
wxLogTrace(_T("i18n"), _T("Looking for \"%s.mo\" in \"%s\""),
szName, searchPath.c_str());
wxFileName fn(szName);
fn.SetExt(_T("mo"));
wxString strFullName;
if ( !wxFindFileInPath(&strFullName, searchPath, fn.GetFullPath()) ) {
wxLogVerbose(_("catalog file for domain '%s' not found."), szName);
wxLogTrace(_T("i18n"), _T("Catalog \"%s.mo\" not found"), szName);
return false;
}
wxLogVerbose(_("using catalog '%s' from '%s'."), szName, strFullName.c_str());
wxLogTrace(_T("i18n"), _T("Using catalog \"%s\"."), strFullName.c_str());
wxFile fileMsg(strFullName);
if ( !fileMsg.IsOpened() )
return false;
wxFileOffset lenFile = fileMsg.Length();
if ( lenFile == wxInvalidOffset )
return false;
size_t nSize = wx_truncate_cast(size_t, lenFile);
wxASSERT_MSG( nSize == lenFile + size_t(0), _T("message catalog bigger than 4GB?") );
char* pData = new char[nSize];
if ( fileMsg.Read(pData, nSize) != lenFile ) {
wxDELETEA(pData);
return false;
}
bool b = Load(pData, nSize, 0, rPluralFormsCalculator);
wxDELETEA(pData);
return b;
}
void MyMsgCatalogMemory::FillHash(MyMessagesHash& hash,
const wxString& msgIdCharset,
bool convertEncoding) const
{
#if wxUSE_UNICODE
convertEncoding = true;
#elif wxUSE_FONTMAP
if ( convertEncoding )
{
wxFontEncoding encCat = wxFontMapperBase::GetEncodingFromName(m_charset);
if ( encCat == wxLocale::GetSystemEncoding() )
{
convertEncoding = false;
}
}
#endif
#if wxUSE_WCHAR_T
wxMBConv *inputConv,
*inputConvPtr = NULL;
if ( convertEncoding && !m_charset.empty() )
{
inputConvPtr =
inputConv = new wxCSConv(m_charset);
}
else
{
#if wxUSE_UNICODE
inputConv = wxConvCurrent;
#else
inputConv = NULL;
#endif
}
wxCSConv *sourceConv = msgIdCharset.empty() || (msgIdCharset == m_charset)
? NULL
: new wxCSConv(msgIdCharset);
#elif wxUSE_FONTMAP
wxASSERT_MSG( msgIdCharset.empty(),
_T("non-ASCII msgid languages only supported if wxUSE_WCHAR_T=1") );
wxEncodingConverter converter;
if ( convertEncoding )
{
wxFontEncoding targetEnc = wxFONTENCODING_SYSTEM;
wxFontEncoding enc = wxFontMapperBase::Get()->CharsetToEncoding(m_charset, false);
if ( enc == wxFONTENCODING_SYSTEM )
{
convertEncoding = false;
}
else
{
targetEnc = wxLocale::GetSystemEncoding();
if (targetEnc == wxFONTENCODING_SYSTEM)
{
wxFontEncodingArray a = wxEncodingConverter::GetPlatformEquivalents(enc);
if (a[0] == enc)
convertEncoding = false;
if (a.GetCount() == 0)
convertEncoding = false;
targetEnc = a[0];
}
}
if ( convertEncoding )
{
converter.Init(enc, targetEnc);
}
}
#endif
(void)convertEncoding;
for (size_t32 i = 0; i < m_numStrings; i++)
{
const char *data = StringAtOfs(m_pOrigTable, i);
wxString msgid;
#if wxUSE_UNICODE
msgid = wxString(data, *inputConv);
#else
#if wxUSE_WCHAR_T
if ( inputConv && sourceConv )
msgid = wxString(inputConv->cMB2WC(data), *sourceConv);
else
#endif
msgid = data;
#endif
data = StringAtOfs(m_pTransTable, i);
size_t length = Swap(m_pTransTable[i].nLen);
size_t offset = 0;
size_t index = 0;
while (offset < length)
{
const char * const str = data + offset;
wxString msgstr;
#if wxUSE_UNICODE
msgstr = wxString(str, *inputConv);
#elif wxUSE_WCHAR_T
if ( inputConv )
msgstr = wxString(inputConv->cMB2WC(str), *wxConvUI);
else
msgstr = str;
#else
#if wxUSE_FONTMAP
if ( convertEncoding )
msgstr = wxString(converter.Convert(str));
else
#endif
msgstr = str;
#endif
if ( !msgstr.empty() )
{
hash[index == 0 ? msgid : msgid + wxChar(index)] = msgstr;
}
offset += strlen(str) + 1;
++index;
}
}
#if wxUSE_WCHAR_T
delete sourceConv;
delete inputConvPtr;
#endif
}
MyMsgCatalog::~MyMsgCatalog()
{
if ( m_conv )
{
if ( wxConvUI == m_conv )
{
wxConvUI = &wxConvLocal;
}
delete m_conv;
}
}
bool MyMsgCatalog::Load(const wxChar *szName,
const char* pCatalogData, size_t nCatalogDataLen, size_t nCatalogUncomp,
const wxChar *msgIdCharset, bool bConvertEncoding)
{
MyMsgCatalogMemory memfile;
m_name = szName;
if ( !memfile.Load(pCatalogData, nCatalogDataLen, nCatalogUncomp, m_pluralFormsCalculator) )
return false;
memfile.FillHash(m_messages, msgIdCharset, bConvertEncoding);
#if wxUSE_WCHAR_T
if ( !bConvertEncoding &&
!memfile.GetCharset().empty() &&
wxConvUI == &wxConvLocal )
{
wxConvUI = m_conv = new wxCSConv(memfile.GetCharset());
}
#endif
return true;
}
bool MyMsgCatalog::LoadFile(const wxChar *szDirPrefix, const wxChar *szName,
const wxChar *msgIdCharset, bool bConvertEncoding)
{
MyMsgCatalogMemory file;
m_name = szName;
if ( !file.LoadFile(szDirPrefix, szName, m_pluralFormsCalculator) )
return false;
file.FillHash(m_messages, msgIdCharset, bConvertEncoding);
#if wxUSE_WCHAR_T
if ( !bConvertEncoding &&
!file.GetCharset().empty() &&
wxConvUI == &wxConvLocal )
{
wxConvUI =
m_conv = new wxCSConv(file.GetCharset());
}
#endif
return true;
}
const wxChar *MyMsgCatalog::GetString(const wxChar *sz, size_t n) const
{
int index = 0;
if (n != size_t(-1))
{
index = m_pluralFormsCalculator->evaluate(n);
}
MyMessagesHash::const_iterator i;
if (index != 0)
{
i = m_messages.find(wxString(sz) + wxChar(index));
}
else
{
i = m_messages.find(sz);
}
if ( i != m_messages.end() )
{
return i->second.c_str();
}
else
return NULL;
}
MyLocale::MyLocale()
: wxLocale()
{
DoMyCommonInit();
}
MyLocale::MyLocale(const wxChar *szName, const wxChar *szShort,
const wxChar *szLocale, bool bLoadDefault, bool bConvertEncoding)
: wxLocale(szName, szShort, szLocale, bLoadDefault, bConvertEncoding)
{
DoMyCommonInit();
}
MyLocale::MyLocale(int language, int flags)
: wxLocale(language, flags)
{
DoMyCommonInit();
}
void MyLocale::DoMyCommonInit()
{
m_pMsgCat = NULL;
}
MyLocale::~MyLocale()
{
MyMsgCatalog *pTmpCat;
while ( m_pMsgCat != NULL ) {
pTmpCat = m_pMsgCat;
m_pMsgCat = m_pMsgCat->m_pNext;
delete pTmpCat;
}
}
const wxChar *MyLocale::GetString(const wxChar *szOrigString,
const wxChar *szDomain) const
{
return GetString(szOrigString, szOrigString, size_t(-1), szDomain);
}
const wxChar *MyLocale::GetString(const wxChar *szOrigString,
const wxChar *szOrigString2,
size_t n,
const wxChar *szDomain) const
{
if ( wxIsEmpty(szOrigString) )
return wxEmptyString;
const wxChar *pszTrans = NULL;
MyMsgCatalog *pMsgCat;
if ( szDomain != NULL && szDomain[0] )
{
pMsgCat = FindCatalog(szDomain);
if ( pMsgCat != NULL )
pszTrans = pMsgCat->GetString(szOrigString, n);
}
else
{
for ( pMsgCat = m_pMsgCat; pMsgCat != NULL; pMsgCat = pMsgCat->m_pNext )
{
pszTrans = pMsgCat->GetString(szOrigString, n);
if ( pszTrans != NULL )
break;
}
}
if ( pszTrans == NULL )
{
#ifdef __WXDEBUG__
wxLogTrace(_T("i18n"),
_T("string \"%s\"[%ld] not found in %slocale '%s'."),
szOrigString, (long)n,
szDomain ? wxString::Format(_T("domain '%s' "), szDomain).c_str()
: _T(""),
GetLocale());
#endif
if (n == size_t(-1))
return szOrigString;
else
return n == 1 ? szOrigString : szOrigString2;
}
return pszTrans;
}
wxString MyLocale::GetHeaderValue( const wxChar* szHeader,
const wxChar* szDomain ) const
{
if ( wxIsEmpty(szHeader) )
return wxEmptyString;
wxChar const * pszTrans = NULL;
MyMsgCatalog *pMsgCat;
if ( szDomain != NULL )
{
pMsgCat = FindCatalog(szDomain);
if ( pMsgCat == NULL )
return wxEmptyString;
pszTrans = pMsgCat->GetString(wxEmptyString, (size_t)-1);
}
else
{
for ( pMsgCat = m_pMsgCat; pMsgCat != NULL; pMsgCat = pMsgCat->m_pNext )
{
pszTrans = pMsgCat->GetString(wxEmptyString, (size_t)-1);
if ( pszTrans != NULL )
break;
}
}
if ( wxIsEmpty(pszTrans) )
return wxEmptyString;
wxChar const * pszFound = wxStrstr(pszTrans, szHeader);
if ( pszFound == NULL )
return wxEmptyString;
pszFound += wxStrlen(szHeader) + 2 ;
wxChar const * pszEndLine = wxStrchr(pszFound, wxT('\n'));
if ( pszEndLine == NULL ) pszEndLine = pszFound + wxStrlen(pszFound);
wxString retVal( pszFound, pszEndLine - pszFound );
return retVal;
}
MyMsgCatalog *MyLocale::FindCatalog(const wxChar *szDomain) const
{
MyMsgCatalog *pMsgCat;
for ( pMsgCat = m_pMsgCat; pMsgCat != NULL; pMsgCat = pMsgCat->m_pNext )
{
if ( wxStricmp(pMsgCat->GetName(), szDomain) == 0 )
return pMsgCat;
}
return NULL;
}
bool MyLocale::IsLoaded(const wxChar *szDomain) const
{
return FindCatalog(szDomain) != NULL;
}
bool MyLocale::AddCatalogFromMemory(const wxChar *szDomain, const MyLocaleMemoryCatalog* msgCatalogMemory)
{
wxString strlocale = GetCanonicalName();
wxString sublocale = strlocale.BeforeFirst(_T('_'));
std::auto_ptr<MyMsgCatalog> pMsgCat (new MyMsgCatalog);
if ( pMsgCat->LoadFile(strlocale.c_str(), szDomain, NULL, true) ) {
pMsgCat->m_pNext = m_pMsgCat;
m_pMsgCat = pMsgCat.release();
return true;
}
for (unsigned int cati = 0; msgCatalogMemory[cati].msgIdLanguage; ++cati)
{
const MyLocaleMemoryCatalog& catlang = msgCatalogMemory[cati];
if (catlang.msgIdLanguage == strlocale ||
catlang.msgIdLanguage == sublocale)
{
if ( pMsgCat->Load(szDomain,
catlang.msgCatalogData, catlang.msgCatalogDataLen, catlang.msgCatalogUncompLen,
catlang.msgIdCharset, true) )
{
wxLogTrace(_T("i18n"), _T("Loading memory catalog for \"%s\"."), szDomain);
pMsgCat->m_pNext = m_pMsgCat;
m_pMsgCat = pMsgCat.release();
return true;
}
}
}
const wxLanguage msgIdLanguage = wxLANGUAGE_ENGLISH_US;
if (GetLanguage() == msgIdLanguage)
return true;
const wxLanguageInfo *msgIdLangInfo = GetLanguageInfo(msgIdLanguage);
if ( msgIdLangInfo && msgIdLangInfo->CanonicalName.Mid(0, 2) == GetCanonicalName().Mid(0, 2) )
{
return true;
}
return false;
}