|
|
|
//=============================================================================
|
|
|
|
//
|
|
|
|
// File : kvi_locale.cpp
|
|
|
|
// Creation date : Fri Mar 19 1999 19:08:41 by Szymon Stefanek
|
|
|
|
//
|
|
|
|
// This file is part of the KVirc irc client distribution
|
|
|
|
// Copyright (C) 1999-2002 Szymon Stefanek (pragma at kvirc dot net)
|
|
|
|
//
|
|
|
|
// This program is FREE software. You can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU General Public License
|
|
|
|
// as published by the Free Software Foundation; either version 2
|
|
|
|
// of the License, or (at your opinion) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the HOPE that it will be USEFUL,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
// See the GNU General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with this program. If not, write to the Free Software Foundation,
|
|
|
|
// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
//
|
|
|
|
//=============================================================================
|
|
|
|
|
|
|
|
#define __KVILIB__
|
|
|
|
|
|
|
|
|
|
|
|
//#define _KVI_DEBUG_CHECK_RANGE_
|
|
|
|
#include "kvi_debug.h"
|
|
|
|
#include "kvi_malloc.h"
|
|
|
|
#include "kvi_bswap.h"
|
|
|
|
|
|
|
|
#define _KVI_LOCALE_CPP_
|
|
|
|
#include "kvi_locale.h"
|
|
|
|
|
|
|
|
#include <tqglobal.h> //for debug()
|
|
|
|
#include <tqtextcodec.h>
|
|
|
|
#include <tqdir.h>
|
|
|
|
|
|
|
|
#ifdef COMPILE_USE_QT4
|
|
|
|
#include <tqlocale.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "kvi_string.h"
|
|
|
|
#include "kvi_qcstring.h"
|
|
|
|
#include "kvi_env.h"
|
|
|
|
#include "kvi_fileutils.h"
|
|
|
|
#include "kvi_file.h"
|
|
|
|
|
|
|
|
|
|
|
|
KVILIB_API KviMessageCatalogue * g_pMainCatalogue = 0;
|
|
|
|
|
|
|
|
static KviStr g_szLang;
|
|
|
|
static KviTranslator * g_pTranslator = 0;
|
|
|
|
static KviPointerHashTable<const char *,KviMessageCatalogue> * g_pCatalogueDict = 0;
|
|
|
|
static TQTextCodec * g_pUtf8TextCodec = 0;
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// The following code was extracted and adapted from gutf8.c
|
|
|
|
// from the GNU GLIB2 package.
|
|
|
|
//
|
|
|
|
// gutf8.c - Operations on UTF-8 strings.
|
|
|
|
//
|
|
|
|
// Copyright (C) 1999 Tom Tromey
|
|
|
|
// Copyright (C) 2000 Red Hat, Inc.
|
|
|
|
//
|
|
|
|
// This library is free software; you can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
|
|
// License as published by the Free Software Foundation; either
|
|
|
|
// version 2 of the License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This library is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
// Lesser General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
|
|
// License along with this library; if not, write to the
|
|
|
|
// Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
// Boston, MA 02110-1301, USA.
|
|
|
|
//
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
typedef char gchar;
|
|
|
|
typedef unsigned char guchar;
|
|
|
|
typedef signed int gssize;
|
|
|
|
typedef unsigned int gunichar;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define UNICODE_VALID(Char) \
|
|
|
|
((Char) < 0x110000 && \
|
|
|
|
(((Char) & 0xFFFFF800) != 0xD800) && \
|
|
|
|
((Char) < 0xFDD0 || (Char) > 0xFDEF) && \
|
|
|
|
((Char) & 0xFFFE) != 0xFFFE)
|
|
|
|
|
|
|
|
#define CONTINUATION_CHAR \
|
|
|
|
if ((*(guchar *)p & 0xc0) != 0x80) /* 10xxxxxx */ \
|
|
|
|
goto error; \
|
|
|
|
val <<= 6; \
|
|
|
|
val |= (*(guchar *)p) & 0x3f;
|
|
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
fast_validate (const char *str)
|
|
|
|
|
|
|
|
{
|
|
|
|
gunichar val = 0;
|
|
|
|
gunichar min = 0;
|
|
|
|
const gchar *p;
|
|
|
|
|
|
|
|
for (p = str; *p; p++)
|
|
|
|
{
|
|
|
|
if (*(guchar *)p < 128)
|
|
|
|
/* done */;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const gchar *last;
|
|
|
|
|
|
|
|
last = p;
|
|
|
|
if ((*(guchar *)p & 0xe0) == 0xc0) /* 110xxxxx */
|
|
|
|
{
|
|
|
|
if ((*(guchar *)p & 0x1e) == 0)
|
|
|
|
goto error;
|
|
|
|
p++;
|
|
|
|
if ((*(guchar *)p & 0xc0) != 0x80) /* 10xxxxxx */
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ((*(guchar *)p & 0xf0) == 0xe0) /* 1110xxxx */
|
|
|
|
{
|
|
|
|
min = (1 << 11);
|
|
|
|
val = *(guchar *)p & 0x0f;
|
|
|
|
goto TWO_REMAINING;
|
|
|
|
}
|
|
|
|
else if ((*(guchar *)p & 0xf8) == 0xf0) /* 11110xxx */
|
|
|
|
{
|
|
|
|
min = (1 << 16);
|
|
|
|
val = *(guchar *)p & 0x07;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
p++;
|
|
|
|
CONTINUATION_CHAR;
|
|
|
|
TWO_REMAINING:
|
|
|
|
p++;
|
|
|
|
CONTINUATION_CHAR;
|
|
|
|
p++;
|
|
|
|
CONTINUATION_CHAR;
|
|
|
|
|
|
|
|
if (val < min)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (!UNICODE_VALID(val))
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
error:
|
|
|
|
return last;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const gchar *
|
|
|
|
fast_validate_len (const char *str,
|
|
|
|
gssize max_len)
|
|
|
|
|
|
|
|
{
|
|
|
|
gunichar val = 0;
|
|
|
|
gunichar min = 0;
|
|
|
|
const gchar *p;
|
|
|
|
|
|
|
|
for (p = str; (max_len < 0 || (p - str) < max_len) && *p; p++)
|
|
|
|
{
|
|
|
|
if (*(guchar *)p < 128)
|
|
|
|
/* done */;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const gchar *last;
|
|
|
|
|
|
|
|
last = p;
|
|
|
|
if ((*(guchar *)p & 0xe0) == 0xc0) /* 110xxxxx */
|
|
|
|
{
|
|
|
|
if (max_len >= 0 && max_len - (p - str) < 2)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if ((*(guchar *)p & 0x1e) == 0)
|
|
|
|
goto error;
|
|
|
|
p++;
|
|
|
|
if ((*(guchar *)p & 0xc0) != 0x80) /* 10xxxxxx */
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ((*(guchar *)p & 0xf0) == 0xe0) /* 1110xxxx */
|
|
|
|
{
|
|
|
|
if (max_len >= 0 && max_len - (p - str) < 3)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
min = (1 << 11);
|
|
|
|
val = *(guchar *)p & 0x0f;
|
|
|
|
goto TWO_REMAINING;
|
|
|
|
}
|
|
|
|
else if ((*(guchar *)p & 0xf8) == 0xf0) /* 11110xxx */
|
|
|
|
{
|
|
|
|
if (max_len >= 0 && max_len - (p - str) < 4)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
min = (1 << 16);
|
|
|
|
val = *(guchar *)p & 0x07;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
p++;
|
|
|
|
CONTINUATION_CHAR;
|
|
|
|
TWO_REMAINING:
|
|
|
|
p++;
|
|
|
|
CONTINUATION_CHAR;
|
|
|
|
p++;
|
|
|
|
CONTINUATION_CHAR;
|
|
|
|
|
|
|
|
if (val < min)
|
|
|
|
goto error;
|
|
|
|
if (!UNICODE_VALID(val))
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
error:
|
|
|
|
return last;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool g_utf8_validate (const char *str,
|
|
|
|
gssize max_len,
|
|
|
|
const gchar **end)
|
|
|
|
|
|
|
|
{
|
|
|
|
const gchar *p;
|
|
|
|
|
|
|
|
if (max_len < 0)
|
|
|
|
p = fast_validate (str);
|
|
|
|
else
|
|
|
|
p = fast_validate_len (str, max_len);
|
|
|
|
|
|
|
|
if (end)
|
|
|
|
*end = p;
|
|
|
|
|
|
|
|
if ((max_len >= 0 && p != str + max_len) ||
|
|
|
|
(max_len < 0 && *p != '\0'))
|
|
|
|
return false;
|
|
|
|
else
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// End of gutf8.c
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
class KviSmartTextCodec : public TQTextCodec
|
|
|
|
{
|
|
|
|
protected:
|
|
|
|
KviTQCString m_szName;
|
|
|
|
TQTextCodec * m_pRecvCodec;
|
|
|
|
TQTextCodec * m_pSendCodec;
|
|
|
|
public:
|
|
|
|
KviSmartTextCodec(const char * szName,const char * szChildCodecName,bool bSendInUtf8)
|
|
|
|
: TQTextCodec()
|
|
|
|
{
|
|
|
|
m_szName = szName;
|
|
|
|
if(!g_pUtf8TextCodec)
|
|
|
|
{
|
|
|
|
g_pUtf8TextCodec = TQTextCodec::codecForName("UTF-8");
|
|
|
|
if(!g_pUtf8TextCodec)
|
|
|
|
{
|
|
|
|
debug("Can't find the global utf8 text codec!");
|
|
|
|
g_pUtf8TextCodec = TQTextCodec::codecForLocale(); // try anything else...
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_pRecvCodec = TQTextCodec::codecForName(szChildCodecName);
|
|
|
|
if(!m_pRecvCodec)
|
|
|
|
{
|
|
|
|
debug("Can't find the codec for name %s (composite codec creation)",szName);
|
|
|
|
m_pRecvCodec = g_pUtf8TextCodec;
|
|
|
|
}
|
|
|
|
if(bSendInUtf8)
|
|
|
|
m_pSendCodec = g_pUtf8TextCodec;
|
|
|
|
else
|
|
|
|
m_pSendCodec = m_pRecvCodec;
|
|
|
|
}
|
|
|
|
public:
|
|
|
|
bool ok(){ return m_pRecvCodec && g_pUtf8TextCodec; };
|
|
|
|
|
|
|
|
virtual int mibEnum () const { return 0; };
|
|
|
|
|
|
|
|
#ifdef COMPILE_USE_QT4
|
|
|
|
virtual TQByteArray name() const { return m_szName; };
|
|
|
|
protected:
|
|
|
|
virtual TQByteArray convertFromUnicode(const TQChar * input,int number,ConverterState * state) const
|
|
|
|
{
|
|
|
|
return m_pSendCodec->fromUnicode(input,number,state);
|
|
|
|
}
|
|
|
|
virtual TQString convertToUnicode(const char * chars,int len,ConverterState * state) const
|
|
|
|
{
|
|
|
|
if(g_utf8_validate(chars,len,NULL))return g_pUtf8TextCodec->toUnicode(chars,len,state);
|
|
|
|
return m_pRecvCodec->toUnicode(chars,len,state);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
public:
|
|
|
|
virtual const char * mimeName () const { return m_pRecvCodec->mimeName(); };
|
|
|
|
virtual const char * name () const { return m_szName.data(); };
|
|
|
|
virtual TQTextDecoder * makeDecoder () const { return m_pRecvCodec->makeDecoder(); };
|
|
|
|
virtual TQTextEncoder * makeEncoder () const { return m_pSendCodec->makeEncoder(); };
|
|
|
|
TQCString fromUnicode (const TQString & uc) const { return m_pSendCodec->fromUnicode(uc); };
|
|
|
|
virtual TQCString fromUnicode (const TQString & uc,int & lenInOut) const { return m_pSendCodec->fromUnicode(uc,lenInOut); };
|
|
|
|
TQString toUnicode(const char * chars) const
|
|
|
|
{
|
|
|
|
if(g_utf8_validate(chars,-1,NULL))return g_pUtf8TextCodec->toUnicode(chars);
|
|
|
|
return m_pRecvCodec->toUnicode(chars);
|
|
|
|
};
|
|
|
|
virtual TQString toUnicode(const char * chars,int len) const
|
|
|
|
{
|
|
|
|
if(g_utf8_validate(chars,len,NULL))return g_pUtf8TextCodec->toUnicode(chars,len);
|
|
|
|
return m_pRecvCodec->toUnicode(chars,len);
|
|
|
|
};
|
|
|
|
TQString toUnicode(const TQByteArray & a,int len) const
|
|
|
|
{
|
|
|
|
if(g_utf8_validate(a.data(),len,NULL))return g_pUtf8TextCodec->toUnicode(a,len);
|
|
|
|
return m_pRecvCodec->toUnicode(a,len);
|
|
|
|
};
|
|
|
|
TQString toUnicode(const TQByteArray & a) const
|
|
|
|
{
|
|
|
|
if(g_utf8_validate(a.data(),a.size(),NULL))return g_pUtf8TextCodec->toUnicode(a);
|
|
|
|
return m_pRecvCodec->toUnicode(a);
|
|
|
|
};
|
|
|
|
TQString toUnicode(const TQCString & a,int len) const
|
|
|
|
{
|
|
|
|
if(g_utf8_validate(a.data(),len,NULL))return g_pUtf8TextCodec->toUnicode(a,len);
|
|
|
|
return m_pRecvCodec->toUnicode(a,len);
|
|
|
|
};
|
|
|
|
TQString toUnicode(const TQCString & a) const
|
|
|
|
{
|
|
|
|
if(g_utf8_validate(a.data(),-1,NULL))return g_pUtf8TextCodec->toUnicode(a);
|
|
|
|
return m_pRecvCodec->toUnicode(a);
|
|
|
|
};
|
|
|
|
|
|
|
|
virtual bool canEncode(TQChar ch) const { return m_pSendCodec->canEncode(ch); };
|
|
|
|
virtual bool canEncode(const TQString &s) const { return m_pSendCodec->canEncode(s); };
|
|
|
|
virtual int heuristicContentMatch(const char * chars,int len) const
|
|
|
|
{
|
|
|
|
int iii = g_pUtf8TextCodec->heuristicContentMatch(chars,len);
|
|
|
|
if(iii < 0)return m_pRecvCodec->heuristicContentMatch(chars,len);
|
|
|
|
return iii;
|
|
|
|
}
|
|
|
|
virtual int heuristicNameMatch(const char * hint) const { return 0; };
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
static KviPointerHashTable<const char *,KviSmartTextCodec> * g_pSmartCodecDict = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// The following code was extracted and adapted from gettext.h and gettextP.h
|
|
|
|
// from the GNU gettext package.
|
|
|
|
//
|
|
|
|
// Internal header for GNU gettext internationalization functions.
|
|
|
|
// Copyright (C) 1995, 1997 Free Software Foundation, Inc.
|
|
|
|
//
|
|
|
|
// This program is free software; you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation; either version 2, or (at your option)
|
|
|
|
// any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Library General Public
|
|
|
|
// License along with the GNU C Library; see the file COPYING.LIB. If not,
|
|
|
|
// write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
// Boston, MA 02110-1301, USA.
|
|
|
|
//
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#if HAVE_LIMITS_H || _LIBC
|
|
|
|
#include <limits.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// The magic number of the GNU message catalog format.
|
|
|
|
#define KVI_LOCALE_MAGIC 0x950412de
|
|
|
|
#define KVI_LOCALE_MAGIC_SWAPPED 0xde120495
|
|
|
|
|
|
|
|
// Revision number of the currently used .mo (binary) file format.
|
|
|
|
#define MO_REVISION_NUMBER 0
|
|
|
|
|
|
|
|
|
|
|
|
// Header for binary .mo file format.
|
|
|
|
struct GnuMoFileHeader
|
|
|
|
{
|
|
|
|
// The magic number.
|
|
|
|
kvi_u32_t magic;
|
|
|
|
// The revision number of the file format.
|
|
|
|
kvi_u32_t revision;
|
|
|
|
// The number of strings pairs.
|
|
|
|
kvi_u32_t nstrings;
|
|
|
|
// Offset of table with start offsets of original strings.
|
|
|
|
kvi_u32_t orig_tab_offset;
|
|
|
|
// Offset of table with start offsets of translation strings.
|
|
|
|
kvi_u32_t trans_tab_offset;
|
|
|
|
// Size of hashing table.
|
|
|
|
kvi_u32_t hash_tab_size;
|
|
|
|
// Offset of first hashing entry.
|
|
|
|
kvi_u32_t hash_tab_offset;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct GnuMoStringDescriptor
|
|
|
|
{
|
|
|
|
// Length of addressed string.
|
|
|
|
kvi_u32_t length;
|
|
|
|
// Offset of string in file.
|
|
|
|
kvi_u32_t offset;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define KVI_SWAP_IF_NEEDED(flag,value) (flag ? kvi_swap32(value) : (value))
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// End of gettext.h & gettextP.h
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
// HELPERS
|
|
|
|
|
|
|
|
static int somePrimeNumbers[90]=
|
|
|
|
{
|
|
|
|
257 , 521 , 769 , 1031, 1087, 1091, 1103, 1117, 1123, 1151, // Incomplete *.mo files
|
|
|
|
1163, 1171, 1181, 1193, 1201, 1213, 1217, 1223, 1229, 1231, // Complete *.mo files
|
|
|
|
1237, 1249, 1259, 1277, 1283, 1289, 1291, 1297, 1307, 1319,
|
|
|
|
1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1433,
|
|
|
|
1447, 1459, 1471, 1481, 1493, 1511, 1523, 1531, 1543, 1553,
|
|
|
|
1567, 1571, 1583, 1597, 1609, 1619, 1627, 1637, 1657, 1667, // Too big for KVIrc *.mo files
|
|
|
|
1693, 1709, 1721, 1733, 1741, 1753, 1777, 1789, 1811, 1831,
|
|
|
|
1907, 2069, 2111, 2221, 2309, 2441, 2531, 2617, 2731, 2837,
|
|
|
|
2903, 3121, 3329, 3331, 3767, 4127, 5051, 6089, 7039, 9973
|
|
|
|
};
|
|
|
|
|
|
|
|
int kvi_getFirstBiggerPrime(int number)
|
|
|
|
{
|
|
|
|
for(int i=0;i<90;i++){
|
|
|
|
if(somePrimeNumbers[i] >= number)return somePrimeNumbers[i];
|
|
|
|
}
|
|
|
|
return 9973; //error!
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
KviMessageCatalogue::KviMessageCatalogue()
|
|
|
|
{
|
|
|
|
//m_uEncoding = 0;
|
|
|
|
m_pTextCodec = TQTextCodec::codecForLocale();
|
|
|
|
|
|
|
|
//m_pMessages = new KviPointerHashTable<const char *,KviTranslationEntry>(1123,true,false); // dictSize, case sensitive , don't copy keys
|
|
|
|
m_pMessages = new KviPointerHashTable<const char *,KviTranslationEntry>(32,true,false); // dictSize, case sensitive , don't copy keys
|
|
|
|
m_pMessages->setAutoDelete(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
KviMessageCatalogue::~KviMessageCatalogue()
|
|
|
|
{
|
|
|
|
if(m_pMessages)
|
|
|
|
delete m_pMessages;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KviMessageCatalogue::load(const TQString& name)
|
|
|
|
{
|
|
|
|
TQString szCatalogueFile(name);
|
|
|
|
|
|
|
|
// Try to load the header
|
|
|
|
KviFile f(szCatalogueFile);
|
|
|
|
if(!f.openForReading())
|
|
|
|
{
|
|
|
|
debug("[KviLocale]: Failed to open the messages file %s: probably doesn't exist",KviTQString::toUtf8(szCatalogueFile).data());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
GnuMoFileHeader hdr;
|
|
|
|
|
|
|
|
if(f.readBlock((char *)&hdr,sizeof(GnuMoFileHeader)) < (int)sizeof(GnuMoFileHeader))
|
|
|
|
{
|
|
|
|
debug("KviLocale: Failed to read header of %s",KviTQString::toUtf8(szCatalogueFile).data());
|
|
|
|
f.close();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool bMustSwap = false;
|
|
|
|
|
|
|
|
if(hdr.magic != KVI_LOCALE_MAGIC)
|
|
|
|
{
|
|
|
|
if(hdr.magic == KVI_LOCALE_MAGIC_SWAPPED)
|
|
|
|
{
|
|
|
|
debug("KviLocale: Swapped magic for file %s: swapping data too",KviTQString::toUtf8(szCatalogueFile).data());
|
|
|
|
bMustSwap = true;
|
|
|
|
} else {
|
|
|
|
debug("KviLocale: Bad locale magic for file %s: not a *.mo file ?",KviTQString::toUtf8(szCatalogueFile).data());
|
|
|
|
f.close();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(KVI_SWAP_IF_NEEDED(bMustSwap,hdr.revision) != MO_REVISION_NUMBER)
|
|
|
|
{
|
|
|
|
debug("KviLocale: Invalid *.mo file revision number for file %s",KviTQString::toUtf8(szCatalogueFile).data());
|
|
|
|
f.close();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int numberOfStrings = KVI_SWAP_IF_NEEDED(bMustSwap,hdr.nstrings);
|
|
|
|
|
|
|
|
if(numberOfStrings <= 0)
|
|
|
|
{
|
|
|
|
debug("KviLocale: No translated messages found in file %s",KviTQString::toUtf8(szCatalogueFile).data());
|
|
|
|
f.close();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(numberOfStrings >= 9972)
|
|
|
|
{
|
|
|
|
debug("Number of strings too big...sure that it is a KVIrc catalog file ?");
|
|
|
|
numberOfStrings = 9972;
|
|
|
|
}
|
|
|
|
|
|
|
|
// return back
|
|
|
|
f.seek(0);
|
|
|
|
|
|
|
|
unsigned int fSize = f.size();
|
|
|
|
char * buffer = (char *)kvi_malloc(fSize);
|
|
|
|
|
|
|
|
// FIXME: maybe read it in blocks eh ?
|
|
|
|
if(f.readBlock(buffer,fSize) < (int)fSize)
|
|
|
|
{
|
|
|
|
debug("KviLocale: Error while reading the translation file %s",KviTQString::toUtf8(szCatalogueFile).data());
|
|
|
|
kvi_free(buffer);
|
|
|
|
f.close();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for broken *.mo files
|
|
|
|
if(fSize < (24 + (sizeof(GnuMoStringDescriptor) * numberOfStrings)))
|
|
|
|
{
|
|
|
|
debug("KviLocale: Broken translation file %s (too small for all descriptors)",KviTQString::toUtf8(szCatalogueFile).data());
|
|
|
|
kvi_free(buffer);
|
|
|
|
f.close();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
GnuMoStringDescriptor * origDescriptor = (GnuMoStringDescriptor *)(buffer + KVI_SWAP_IF_NEEDED(bMustSwap,hdr.orig_tab_offset));
|
|
|
|
GnuMoStringDescriptor * transDescriptor = (GnuMoStringDescriptor *)(buffer + KVI_SWAP_IF_NEEDED(bMustSwap,hdr.trans_tab_offset));
|
|
|
|
|
|
|
|
// Check again for broken *.mo files
|
|
|
|
int expectedFileSize = KVI_SWAP_IF_NEEDED(bMustSwap,transDescriptor[numberOfStrings - 1].offset) +
|
|
|
|
KVI_SWAP_IF_NEEDED(bMustSwap,transDescriptor[numberOfStrings - 1].length);
|
|
|
|
|
|
|
|
if(fSize < (unsigned int)expectedFileSize)
|
|
|
|
{
|
|
|
|
debug("KviLocale: Broken translation file %s (too small for all the message strings)",KviTQString::toUtf8(szCatalogueFile).data());
|
|
|
|
kvi_free(buffer);
|
|
|
|
f.close();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ok...we can run now
|
|
|
|
|
|
|
|
int dictSize = kvi_getFirstBiggerPrime(numberOfStrings);
|
|
|
|
if(m_pMessages)
|
|
|
|
delete m_pMessages;
|
|
|
|
m_pMessages = new KviPointerHashTable<const char *,KviTranslationEntry>(dictSize,true,false); // dictSize, case sensitive , don't copy keys
|
|
|
|
m_pMessages->setAutoDelete(true);
|
|
|
|
|
|
|
|
KviStr szHeader;
|
|
|
|
|
|
|
|
for(int i=0;i < numberOfStrings;i++)
|
|
|
|
{
|
|
|
|
// FIXME: "Check for NULL inside strings here ?"
|
|
|
|
//debug("original seems to be at %u and %u byttes long",KVI_SWAP_IF_NEEDED(bMustSwap,origDescriptor[i].offset),
|
|
|
|
// KVI_SWAP_IF_NEEDED(bMustSwap,origDescriptor[i].length));
|
|
|
|
//debug("translated seems to be at %u and %u byttes long",KVI_SWAP_IF_NEEDED(bMustSwap,transDescriptor[i].offset),
|
|
|
|
// KVI_SWAP_IF_NEEDED(bMustSwap,transDescriptor[i].length));
|
|
|
|
|
|
|
|
KviTranslationEntry * e = new KviTranslationEntry(
|
|
|
|
(char *)(buffer + KVI_SWAP_IF_NEEDED(bMustSwap,origDescriptor[i].offset)),
|
|
|
|
KVI_SWAP_IF_NEEDED(bMustSwap,origDescriptor[i].length),
|
|
|
|
(char *)(buffer + KVI_SWAP_IF_NEEDED(bMustSwap,transDescriptor[i].offset)),
|
|
|
|
KVI_SWAP_IF_NEEDED(bMustSwap,transDescriptor[i].length));
|
|
|
|
|
|
|
|
// In some (or all?) *.mo files the first string
|
|
|
|
// is zero bytes long and the translated one contains
|
|
|
|
// informations about the translation
|
|
|
|
if(e->m_szKey.len() == 0)
|
|
|
|
{
|
|
|
|
szHeader = e->m_szEncodedTranslation;
|
|
|
|
delete e;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_pMessages->insert(e->m_szKey.ptr(),e);
|
|
|
|
}
|
|
|
|
|
|
|
|
kvi_free(buffer);
|
|
|
|
f.close();
|
|
|
|
|
|
|
|
m_pTextCodec = 0;
|
|
|
|
|
|
|
|
// find out the text encoding , if possible
|
|
|
|
if(szHeader.hasData())
|
|
|
|
{
|
|
|
|
// find "charset=*\n"
|
|
|
|
int idx = szHeader.findFirstIdx("charset=");
|
|
|
|
if(idx != -1)
|
|
|
|
{
|
|
|
|
szHeader.cutLeft(idx + 8);
|
|
|
|
szHeader.cutFromFirst('\n');
|
|
|
|
szHeader.stripWhiteSpace();
|
|
|
|
m_pTextCodec = KviLocale::codecForName(szHeader.ptr());
|
|
|
|
if(!m_pTextCodec)
|
|
|
|
{
|
|
|
|
debug("Can't find the codec for charset=%s",szHeader.ptr());
|
|
|
|
debug("Falling back to codecForLocale()");
|
|
|
|
m_pTextCodec = TQTextCodec::codecForLocale();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!m_pTextCodec)
|
|
|
|
{
|
|
|
|
debug("The message catalogue does not have a \"charset\" header");
|
|
|
|
debug("Assuming utf8"); // FIXME: or codecForLocale() ?
|
|
|
|
m_pTextCodec = TQTextCodec::codecForName("UTF-8");
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char * KviMessageCatalogue::translate(const char *text)
|
|
|
|
{
|
|
|
|
KviTranslationEntry * aux = m_pMessages->find(text);
|
|
|
|
if(aux)return aux->m_szEncodedTranslation.ptr();
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
|
|
|
const TQString & KviMessageCatalogue::translateToTQString(const char *text)
|
|
|
|
{
|
|
|
|
KviTranslationEntry * aux = m_pMessages->find(text);
|
|
|
|
if(aux)
|
|
|
|
{
|
|
|
|
if(aux->m_pTQTranslation)return *(aux->m_pTQTranslation);
|
|
|
|
aux->m_pTQTranslation = new TQString(m_pTextCodec->toUnicode(aux->m_szEncodedTranslation.ptr()));
|
|
|
|
return *(aux->m_pTQTranslation);
|
|
|
|
}
|
|
|
|
// no translation is available: let's avoid continous string decoding
|
|
|
|
aux = new KviTranslationEntry(text);
|
|
|
|
m_pMessages->insert(aux->m_szKey.ptr(),aux);
|
|
|
|
aux->m_pTQTranslation = new TQString(m_pTextCodec->toUnicode(aux->m_szEncodedTranslation.ptr()));
|
|
|
|
return *(aux->m_pTQTranslation);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace KviLocale
|
|
|
|
{
|
|
|
|
#ifndef TQT_NO_BIG_CODECS
|
|
|
|
#define NUM_ENCODINGS 109
|
|
|
|
#else
|
|
|
|
#define NUM_ENCODINGS 85
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static EncodingDescription supported_encodings[]=
|
|
|
|
{
|
|
|
|
{ "UTF-8" , 0 , 0 , "8-bit Unicode" },
|
|
|
|
{ "ISO-8859-1" , 0 , 0 , "Western, Latin-1" },
|
|
|
|
{ "ISO-8859-2" , 0 , 0 , "Central European 1" },
|
|
|
|
{ "ISO-8859-3" , 0 , 0 , "Central European 2" },
|
|
|
|
{ "ISO-8859-4" , 0 , 0 , "Baltic, Standard" },
|
|
|
|
{ "ISO-8859-5" , 0 , 0 , "Cyrillic, ISO" },
|
|
|
|
{ "ISO-8859-6" , 0 , 0 , "Arabic, Standard" },
|
|
|
|
{ "ISO-8859-7" , 0 , 0 , "Greek" },
|
|
|
|
{ "ISO-8859-8" , 0 , 0 , "Hebrew, visually ordered" },
|
|
|
|
{ "ISO-8859-8-i" , 0 , 0 , "Hebrew, logically ordered" },
|
|
|
|
{ "ISO-8859-9" , 0 , 0 , "Turkish, Latin-5" },
|
|
|
|
{ "ISO-8859-15" , 0 , 0 , "Western, Latin-1 + Euro" },
|
|
|
|
{ "KOI8-R" , 0 , 0 , "Cyrillic, KOI" },
|
|
|
|
{ "KOI8-U" , 0 , 0 , "Ukrainian" },
|
|
|
|
{ "CP-1250" , 0 , 0 , "Central European 3" },
|
|
|
|
{ "CP-1251" , 0 , 0 , "Cyrillic, Windows" },
|
|
|
|
{ "CP-1252" , 0 , 0 , "Western, CP" },
|
|
|
|
{ "CP-1253" , 0 , 0 , "Greek, CP" },
|
|
|
|
{ "CP-1256" , 0 , 0 , "Arabic, CP" },
|
|
|
|
{ "CP-1257" , 0 , 0 , "Baltic, CP" },
|
|
|
|
{ "CP-1255" , 0 , 0 , "Hebrew, CP" },
|
|
|
|
{ "CP-1254" , 0 , 0 , "Turkish, CP" },
|
|
|
|
{ "TIS-620" , 0 , 0 , "Thai" },
|
|
|
|
#ifndef TQT_NO_BIG_CODECS
|
|
|
|
{ "Big5" , 0 , 0 , "Chinese Traditional" },
|
|
|
|
{ "Big5-HKSCS" , 0 , 0 , "Chinese Traditional, Hong Kong" },
|
|
|
|
{ "GB18030" , 0 , 0 , "Chinese Simplified" },
|
|
|
|
{ "JIS7" , 0 , 0 , "Japanese (JIS7)" },
|
|
|
|
{ "Shift-JIS" , 0 , 0 , "Japanese (Shift-JIS)" },
|
|
|
|
{ "EUC-JP" , 0 , 0 , "Japanese (EUC-JP)" },
|
|
|
|
{ "EUC-KR" , 0 , 0 , "Korean" },
|
|
|
|
{ "TSCII" , 0 , 0 , "Tamil" },
|
|
|
|
#endif
|
|
|
|
{ "ISO-8859-10" , 0 , 0 , "ISO-8859-10" },
|
|
|
|
{ "ISO-8859-13" , 0 , 0 , "ISO-8859-13" },
|
|
|
|
{ "ISO-8859-14" , 0 , 0 , "ISO-8859-14" },
|
|
|
|
{ "IBM-850" , 0 , 0 , "IBM-850" },
|
|
|
|
{ "IBM-866" , 0 , 0 , "IBM-866" },
|
|
|
|
{ "CP874" , 0 , 0 , "CP874" },
|
|
|
|
|
|
|
|
// smart codecs that send in the local charset
|
|
|
|
{ "ISO-8859-1 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Western Latin-1, O: Western Latin-1" },
|
|
|
|
{ "ISO-8859-2 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Central European 1, O: Central European 1" },
|
|
|
|
{ "ISO-8859-3 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Central European 2, O: Central European 2" },
|
|
|
|
{ "ISO-8859-4 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Baltic, Standard, O: Baltic, Standard" },
|
|
|
|
{ "ISO-8859-5 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Cyrillic, ISO, O: Cyrillic, ISO" },
|
|
|
|
{ "ISO-8859-6 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Arabic, Standard, O: Arabic, Standard" },
|
|
|
|
{ "ISO-8859-7 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Greek, O: Greek" },
|
|
|
|
{ "ISO-8859-8 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Hebrew, visually ordered, O: Hebrew, visually ordered" },
|
|
|
|
{ "ISO-8859-8-i [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Hebrew, logically ordered, O: Hebrew, logically ordered" },
|
|
|
|
{ "ISO-8859-9 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Turkish, Latin-5, O: Turkish, Latin-5" },
|
|
|
|
{ "ISO-8859-15 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Western, Latin-1 + Euro, O: Western, Latin-1 + Euro" },
|
|
|
|
{ "KOI8-R [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Cyrillic, KOI, O: Cyrillic, KOI" },
|
|
|
|
{ "KOI8-U [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Ukrainian, O: Ukrainian" },
|
|
|
|
{ "CP-1250 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Central European 3, O: Central European 3" },
|
|
|
|
{ "CP-1251 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Cyrillic, Windows, O: Cyrillic, Windows" },
|
|
|
|
{ "CP-1252 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Western, CP, O: Western, CP" },
|
|
|
|
{ "CP-1253 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Greek, CP, O: Greek, CP" },
|
|
|
|
{ "CP-1256 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Arabic, CP, O: Arabic, CP" },
|
|
|
|
{ "CP-1257 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Baltic, CP, O: Baltic, CP" },
|
|
|
|
{ "CP-1255 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Hebrew, CP, O: Hebrew, CP" },
|
|
|
|
{ "CP-1254 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Turkish, CP, O: Turkish, CP" },
|
|
|
|
{ "TIS-620 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Thai, O: Thai" },
|
|
|
|
#ifndef TQT_NO_BIG_CODECS
|
|
|
|
{ "Big5 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Chinese Traditional, O: Chinese Traditional" },
|
|
|
|
{ "Big5-HKSCS [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Chinese Traditional, Hong Kong, O: Chinese Traditional, Hong Kong" },
|
|
|
|
{ "GB18030 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Chinese Simplified, O: Chinese Simplified" },
|
|
|
|
{ "JIS7 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Japanese (JIS7), O: Japanese " },
|
|
|
|
{ "Shift-JIS [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Japanese (Shift-JIS), O: Japanese (Shift-JIS)" },
|
|
|
|
{ "EUC-JP [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Japanese (EUC-JP), O: Japanese (EUC-JP)" },
|
|
|
|
{ "EUC-KR [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Korean, O: Korean" },
|
|
|
|
{ "TSCII [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Tamil, O: Tamil" },
|
|
|
|
#endif
|
|
|
|
{ "ISO-8859-10 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / ISO-8859-10, O: ISO-8859-10" },
|
|
|
|
{ "ISO-8859-13 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / ISO-8859-13, O: ISO-8859-13" },
|
|
|
|
{ "ISO-8859-14 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / ISO-8859-14, O: ISO-8859-14" },
|
|
|
|
{ "IBM-850 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / IBM-850, O: IBM-850" },
|
|
|
|
{ "IBM-866 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / IBM-866, O: IBM-866" },
|
|
|
|
{ "CP874 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / CP874, O: CP874" },
|
|
|
|
|
|
|
|
// smart codecs that send in utf8
|
|
|
|
{ "UTF-8 [ISO-8859-1]" , 1 , 1 , "I: 8-bit Unicode / Western Latin-1, O: 8-bit Unicode" },
|
|
|
|
{ "UTF-8 [ISO-8859-2]" , 1 , 1 , "I: 8-bit Unicode / Central European 1, O: 8-bit Unicode" },
|
|
|
|
{ "UTF-8 [ISO-8859-3]" , 1 , 1 , "I: 8-bit Unicode / Central European 2, O: 8-bit Unicode" },
|
|
|
|
{ "UTF-8 [ISO-8859-4]" , 1 , 1 , "I: 8-bit Unicode / Baltic, Standard, O: 8-bit Unicode" },
|
|
|
|
|
|
|
|
{ "UTF-8 [ISO-8859-5]" , 1 , 1 , "I: 8-bit Unicode / Cyrillic, ISO, O: 8-bit Unicode" },
|
|
|
|
{ "UTF-8 [ISO-8859-6]" , 1 , 1 , "I: 8-bit Unicode / Arabic, Standard, O: 8-bit Unicode" },
|
|
|
|
{ "UTF-8 [ISO-8859-7]" , 1 , 1 , "I: 8-bit Unicode / Greek, O: 8-bit Unicode" },
|
|
|
|
{ "UTF-8 [ISO-8859-8]" , 1 , 1 , "I: 8-bit Unicode / Hebrew, visually ordered, O: 8-bit Unicode" },
|
|
|
|
|
|
|
|
{ "UTF-8 [ISO-8859-8-i]" , 1 , 1 , "I: 8-bit Unicode / Hebrew, logically ordered, O: 8-bit Unicode" },
|
|
|
|
{ "UTF-8 [ISO-8859-9]" , 1 , 1 , "I: 8-bit Unicode / Turkish, Latin-5, O: 8-bit Unicode" },
|
|
|
|
{ "UTF-8 [ISO-8859-15]" , 1 , 1 , "I: 8-bit Unicode / Western, Latin-1 + Euro, O: 8-bit Unicode" },
|
|
|
|
{ "UTF-8 [KOI8-R]" , 1 , 1 , "I: 8-bit Unicode / Cyrillic, KOI, O: 8-bit Unicode" },
|
|
|
|
|
|
|
|
{ "UTF-8 [KOI8-U]" , 1 , 1 , "I: 8-bit Unicode / Ukrainian, O: 8-bit Unicode" },
|
|
|
|
{ "UTF-8 [CP-1250]" , 1 , 1 , "I: 8-bit Unicode / Central European 3, O: 8-bit Unicode" },
|
|
|
|
{ "UTF-8 [CP-1251]" , 1 , 1 , "I: 8-bit Unicode / Cyrillic, Windows, O: 8-bit Unicode" },
|
|
|
|
{ "UTF-8 [CP-1252]" , 1 , 1 , "I: 8-bit Unicode / Western, CP, O: 8-bit Unicode" },
|
|
|
|
|
|
|
|
{ "UTF-8 [CP-1253]" , 1 , 1 , "I: 8-bit Unicode / Greek, CP, O: 8-bit Unicode" },
|
|
|
|
{ "UTF-8 [CP-1256]" , 1 , 1 , "I: 8-bit Unicode / Arabic, CP, O: 8-bit Unicode" },
|
|
|
|
{ "UTF-8 [CP-1257]" , 1 , 1 , "I: 8-bit Unicode / Baltic, CP, O: 8-bit Unicode" },
|
|
|
|
{ "UTF-8 [CP-1255]" , 1 , 1 , "I: 8-bit Unicode / Hebrew, CP, O: 8-bit Unicode" },
|
|
|
|
|
|
|
|
{ "UTF-8 [CP-1254]" , 1 , 1 , "I: 8-bit Unicode / Turkish, CP, O: 8-bit Unicode" },
|
|
|
|
{ "UTF-8 [TIS-620]" , 1 , 1 , "I: 8-bit Unicode / Thai, O: 8-bit Unicode" },
|
|
|
|
#ifndef TQT_NO_BIG_CODECS
|
|
|
|
{ "UTF-8 [Big5]" , 1 , 1 , "I: 8-bit Unicode / Chinese Traditional, O: 8-bit Unicode" },
|
|
|
|
{ "UTF-8 [Big5-HKSCS]" , 1 , 1 , "I: 8-bit Unicode / Chinese Traditional, Hong Kong, O: 8-bit Unicode" },
|
|
|
|
|
|
|
|
{ "UTF-8 [GB18030]" , 1 , 1 , "I: 8-bit Unicode / Chinese Simplified, O: 8-bit Unicode" },
|
|
|
|
{ "UTF-8 [JIS7]" , 1 , 1 , "I: 8-bit Unicode / Japanese (JIS7), O: 8-bit Unicode" },
|
|
|
|
{ "UTF-8 [Shift-JIS]" , 1 , 1 , "I: 8-bit Unicode / Japanese (Shift-JIS), O: Japanese (Shift-JIS)" },
|
|
|
|
{ "UTF-8 [EUC-JP]" , 1 , 1 , "I: 8-bit Unicode / Japanese (EUC-JP), O: Japanese (EUC-JP)" },
|
|
|
|
|
|
|
|
{ "UTF-8 [EUC-KR]" , 1 , 1 , "I: 8-bit Unicode / Korean, O: 8-bit Unicode" },
|
|
|
|
{ "UTF-8 [TSCII]" , 1 , 1 , "I: 8-bit Unicode / Tamil, O: 8-bit Unicode" },
|
|
|
|
#endif
|
|
|
|
{ "UTF-8 [ISO-8859-10]" , 1 , 1 , "I: 8-bit Unicode / ISO-8859-10, O: 8-bit Unicode" },
|
|
|
|
{ "UTF-8 [ISO-8859-13]" , 1 , 1 , "I: 8-bit Unicode / ISO-8859-13, O: 8-bit Unicode" },
|
|
|
|
|
|
|
|
{ "UTF-8 [ISO-8859-14]" , 1 , 1 , "I: 8-bit Unicode / ISO-8859-14, O: 8-bit Unicode" },
|
|
|
|
{ "UTF-8 [IBM-850]" , 1 , 1 , "I: 8-bit Unicode / IBM-850, O: 8-bit Unicode" },
|
|
|
|
{ "UTF-8 [IBM-866]" , 1 , 1 , "I: 8-bit Unicode / IBM-866, O: 8-bit Unicode" },
|
|
|
|
{ "UTF-8 [CP874]" , 1 , 1 , "I: 8-bit Unicode / CP874, O: 8-bit Unicode" },
|
|
|
|
|
|
|
|
{ 0 , 0 , 0 , 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
EncodingDescription * encodingDescription(int iIdx)
|
|
|
|
{
|
|
|
|
if(iIdx > NUM_ENCODINGS)return &(supported_encodings[NUM_ENCODINGS]);
|
|
|
|
return &(supported_encodings[iIdx]);
|
|
|
|
}
|
|
|
|
|
|
|
|
TQTextCodec * codecForName(const char * szName)
|
|
|
|
{
|
|
|
|
KviStr szTmp = szName;
|
|
|
|
int idx = szTmp.findFirstIdx('[');
|
|
|
|
if(idx != -1)
|
|
|
|
{
|
|
|
|
// composite codec: either UTF-8 [child codec] or child codec [UTF-8]
|
|
|
|
KviSmartTextCodec * c = g_pSmartCodecDict->find(szName);
|
|
|
|
if(c)return c;
|
|
|
|
|
|
|
|
|
|
|
|
if(kvi_strEqualCIN("UTF-8 [",szName,7))
|
|
|
|
{
|
|
|
|
szTmp.replaceAll("UTF-8 [","");
|
|
|
|
szTmp.replaceAll("]","");
|
|
|
|
// smart codec that sends UTF-8
|
|
|
|
c = new KviSmartTextCodec(szName,szTmp.ptr(),true);
|
|
|
|
} else {
|
|
|
|
szTmp.cutFromFirst(' ');
|
|
|
|
// smart codec that sends child encoding
|
|
|
|
c = new KviSmartTextCodec(szName,szTmp.ptr(),false);
|
|
|
|
}
|
|
|
|
if(c->ok())
|
|
|
|
{
|
|
|
|
g_pSmartCodecDict->replace(szName,c);
|
|
|
|
return c;
|
|
|
|
} else {
|
|
|
|
delete c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return TQTextCodec::codecForName(szName);
|
|
|
|
}
|
|
|
|
|
|
|
|
const KviStr & localeName()
|
|
|
|
{
|
|
|
|
return g_szLang;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool loadCatalogue(const TQString &name,const TQString &szLocaleDir)
|
|
|
|
{
|
|
|
|
//debug("Looking up catalogue %s",name);
|
|
|
|
if(g_pCatalogueDict->find(KviTQString::toUtf8(name).data()))return true; // already loaded
|
|
|
|
|
|
|
|
TQString szBuffer;
|
|
|
|
|
|
|
|
if(findCatalogue(szBuffer,name,szLocaleDir))
|
|
|
|
{
|
|
|
|
KviMessageCatalogue * c = new KviMessageCatalogue();
|
|
|
|
if(c->load(szBuffer))
|
|
|
|
{
|
|
|
|
//debug("KviLocale: loaded catalogue %s",name);
|
|
|
|
g_pCatalogueDict->insert(KviTQString::toUtf8(name).data(),c);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool unloadCatalogue(const TQString &name)
|
|
|
|
{
|
|
|
|
//debug("Unloading catalogue : %s",name);
|
|
|
|
return g_pCatalogueDict->remove(KviTQString::toUtf8(name).data());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool findCatalogue(TQString &szBuffer,const TQString& name,const TQString& szLocaleDir)
|
|
|
|
{
|
|
|
|
KviStr szLocale = g_szLang;
|
|
|
|
|
|
|
|
TQString szLocDir = szLocaleDir;
|
|
|
|
KviTQString::ensureLastCharIs(szLocDir,KVI_PATH_SEPARATOR_CHAR);
|
|
|
|
|
|
|
|
KviTQString::sprintf(szBuffer,"%Q%TQ_%s.mo",&szLocDir,&name,szLocale.ptr());
|
|
|
|
|
|
|
|
if(KviFileUtils::fileExists(szBuffer))return true;
|
|
|
|
|
|
|
|
if(szLocale.findFirstIdx('.') != -1)
|
|
|
|
{
|
|
|
|
// things like en_GB.utf8
|
|
|
|
// kill them
|
|
|
|
szLocale.cutFromFirst('.');
|
|
|
|
|
|
|
|
KviTQString::sprintf(szBuffer,"%Q%TQ_%s.mo",&szLocDir,&name,szLocale.ptr());
|
|
|
|
if(KviFileUtils::fileExists(szBuffer))return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(szLocale.findFirstIdx('@') != -1)
|
|
|
|
{
|
|
|
|
// things like @euro ?
|
|
|
|
// kill them
|
|
|
|
szLocale.cutFromFirst('@');
|
|
|
|
KviTQString::sprintf(szBuffer,"%Q%TQ_%s.mo",&szLocDir,&name,szLocale.ptr());
|
|
|
|
if(KviFileUtils::fileExists(szBuffer))return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(szLocale.findFirstIdx('_') != -1)
|
|
|
|
{
|
|
|
|
// things like en_GB
|
|
|
|
// kill them
|
|
|
|
szLocale.cutFromFirst('_');
|
|
|
|
KviTQString::sprintf(szBuffer,"%Q%TQ_%s.mo",&szLocDir,&name,szLocale.ptr());
|
|
|
|
if(KviFileUtils::fileExists(szBuffer))return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// try the lower case version too
|
|
|
|
szLocale.toLower();
|
|
|
|
KviTQString::sprintf(szBuffer,"%Q%TQ_%s.mo",&szLocDir,&name,szLocale.ptr());
|
|
|
|
if(KviFileUtils::fileExists(szBuffer))return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// This function attempts to determine the current locale
|
|
|
|
// and then load the corresponding translation file
|
|
|
|
// from the KVIrc locale directory
|
|
|
|
// Returns true if the locale was correctly set
|
|
|
|
// i.e. the locale is C or POSIX (no translation needed)
|
|
|
|
// or the locale is correctly defined and the
|
|
|
|
// translation map was sucesfully loaded
|
|
|
|
//
|
|
|
|
|
|
|
|
void init(TQApplication * app,const TQString &localeDir)
|
|
|
|
{
|
|
|
|
// first of all try to find out the current locale
|
|
|
|
g_szLang="";
|
|
|
|
#ifdef COMPILE_USE_QT4
|
|
|
|
TQString szLangFile=TQString("%1/.kvirc_force_locale").tqarg(TQDir::homePath());
|
|
|
|
#else
|
|
|
|
TQString szLangFile=TQString("%1/.kvirc_force_locale").tqarg(TQDir::homeDirPath());
|
|
|
|
#endif
|
|
|
|
if(KviFileUtils::fileExists(szLangFile))
|
|
|
|
{
|
|
|
|
TQString szTmp;
|
|
|
|
KviFileUtils::readFile(szLangFile,szTmp);
|
|
|
|
g_szLang=szTmp;
|
|
|
|
}
|
|
|
|
if(g_szLang.isEmpty())g_szLang = kvi_getenv("KVIRC_LANG");
|
|
|
|
#ifdef COMPILE_USE_QT4
|
|
|
|
if(g_szLang.isEmpty())g_szLang = TQLocale::system().name();
|
|
|
|
#else
|
|
|
|
if(g_szLang.isEmpty())g_szLang = TQTextCodec::locale();
|
|
|
|
#endif
|
|
|
|
if(g_szLang.isEmpty())g_szLang = kvi_getenv("LC_MESSAGES");
|
|
|
|
if(g_szLang.isEmpty())g_szLang = kvi_getenv("LANG");
|
|
|
|
if(g_szLang.isEmpty())g_szLang = "en";
|
|
|
|
g_szLang.stripWhiteSpace();
|
|
|
|
|
|
|
|
// the main catalogue is supposed to be kvirc_<language>.mo
|
|
|
|
g_pMainCatalogue = new KviMessageCatalogue();
|
|
|
|
// the catalogue dict
|
|
|
|
g_pCatalogueDict = new KviPointerHashTable<const char *,KviMessageCatalogue>;
|
|
|
|
g_pCatalogueDict->setAutoDelete(true);
|
|
|
|
|
|
|
|
// the smart codec dict
|
|
|
|
g_pSmartCodecDict = new KviPointerHashTable<const char *,KviSmartTextCodec>;
|
|
|
|
// the TQt docs explicitly state that we shouldn't delete
|
|
|
|
// the codecs by ourselves...
|
|
|
|
g_pSmartCodecDict->setAutoDelete(false);
|
|
|
|
|
|
|
|
if(g_szLang.hasData())
|
|
|
|
{
|
|
|
|
TQString szBuffer;
|
|
|
|
if(findCatalogue(szBuffer,"kvirc",localeDir))
|
|
|
|
{
|
|
|
|
g_pMainCatalogue->load(szBuffer);
|
|
|
|
g_pTranslator = new KviTranslator(TQT_TQOBJECT(app),"kvirc_translator");
|
|
|
|
app->installTranslator(g_pTranslator);
|
|
|
|
} else {
|
|
|
|
KviStr szTmp = g_szLang;
|
|
|
|
szTmp.cutFromFirst('.');
|
|
|
|
szTmp.cutFromFirst('_');
|
|
|
|
szTmp.cutFromFirst('@');
|
|
|
|
szTmp.toLower();
|
|
|
|
if(!(kvi_strEqualCI(szTmp.ptr(),"en") ||
|
|
|
|
kvi_strEqualCI(szTmp.ptr(),"c") ||
|
|
|
|
kvi_strEqualCI(szTmp.ptr(),"us") ||
|
|
|
|
kvi_strEqualCI(szTmp.ptr(),"gb") ||
|
|
|
|
kvi_strEqualCI(szTmp.ptr(),"posix")))
|
|
|
|
{
|
|
|
|
// FIXME: THIS IS NO LONGER VALID!!!
|
|
|
|
debug("Can't find the catalogue for locale \"%s\" (%s)",g_szLang.ptr(),szTmp.ptr());
|
|
|
|
debug("There is no such translation or the $LANG variable was incorrectly set");
|
|
|
|
debug("You can use $KVIRC_LANG to override the catalogue name");
|
|
|
|
debug("For example you can set KVIRC_LANG to it_IT to force usage of the it.mo catalogue");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//g_pTextCodec = TQTextCodec::codecForLocale();
|
|
|
|
//if(!g_pTextCodec)g_pTextCodec = TQTextCodec::codecForLocale();
|
|
|
|
}
|
|
|
|
|
|
|
|
void done(TQApplication * app)
|
|
|
|
{
|
|
|
|
delete g_pMainCatalogue;
|
|
|
|
delete g_pCatalogueDict;
|
|
|
|
delete g_pSmartCodecDict;
|
|
|
|
g_pMainCatalogue = 0;
|
|
|
|
g_pCatalogueDict = 0;
|
|
|
|
g_pSmartCodecDict = 0;
|
|
|
|
if(g_pTranslator)
|
|
|
|
{
|
|
|
|
app->removeTranslator(g_pTranslator);
|
|
|
|
delete g_pTranslator;
|
|
|
|
g_pTranslator = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
KviMessageCatalogue * getLoadedCatalogue(const TQString& name)
|
|
|
|
{
|
|
|
|
return g_pCatalogueDict->find(KviTQString::toUtf8(name).data());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const char * translate(const char * text,const char * context)
|
|
|
|
{
|
|
|
|
if(context)
|
|
|
|
{
|
|
|
|
KviMessageCatalogue * c = g_pCatalogueDict->find(context);
|
|
|
|
if(!c)
|
|
|
|
{
|
|
|
|
// FIXME: Should really try to load the catalogue here!
|
|
|
|
c = new KviMessageCatalogue();
|
|
|
|
g_pCatalogueDict->insert(context,c);
|
|
|
|
}
|
|
|
|
return c->translate(text);
|
|
|
|
}
|
|
|
|
return g_pMainCatalogue->translate(text);
|
|
|
|
}
|
|
|
|
|
|
|
|
const TQString & translateToTQString(const char * text,const char * context)
|
|
|
|
{
|
|
|
|
if(context)
|
|
|
|
{
|
|
|
|
KviMessageCatalogue * c = g_pCatalogueDict->find(context);
|
|
|
|
if(!c)
|
|
|
|
{
|
|
|
|
// FIXME: Should really try to load the catalogue here!
|
|
|
|
c = new KviMessageCatalogue();
|
|
|
|
g_pCatalogueDict->insert(context,c);
|
|
|
|
}
|
|
|
|
return c->translateToTQString(text);
|
|
|
|
}
|
|
|
|
return g_pMainCatalogue->translateToTQString(text);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
KviTranslator::KviTranslator(TQObject * par,const char * nam)
|
|
|
|
#ifdef COMPILE_USE_QT4
|
|
|
|
: TQTranslator(par)
|
|
|
|
#else
|
|
|
|
: TQTranslator(par,nam)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
KviTranslator::~KviTranslator()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef COMPILE_USE_QT4
|
|
|
|
TQString KviTranslator::translate(const char *context,const char * message,const char * comment) const
|
|
|
|
{
|
|
|
|
// we ignore contexts and comments for qt translations
|
|
|
|
return g_pMainCatalogue->translateToTQString(message);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
TQString KviTranslator::find(const char *context,const char * message) const
|
|
|
|
{
|
|
|
|
// we ignore contexts for qt translations
|
|
|
|
return g_pMainCatalogue->translateToTQString(message);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef COMPILE_USE_QT4
|
|
|
|
TQTranslatorMessage KviTranslator::findMessage(const char * context,const char * sourceText,const char * comment) const
|
|
|
|
{
|
|
|
|
// we ignore contexts for qt translations
|
|
|
|
return TQTranslatorMessage(context,sourceText,comment,g_pMainCatalogue->translateToTQString(sourceText));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
|
|
|
// a fake table that will force these translations
|
|
|
|
// to be included in the *.pot file
|
|
|
|
|
|
|
|
static TQString fake_translations_table[]=
|
|
|
|
{
|
|
|
|
// global
|
|
|
|
__tr2qs("OK"),
|
|
|
|
__tr2qs("Cancel"),
|
|
|
|
// color dialog
|
|
|
|
__tr2qs("Select color"),
|
|
|
|
__tr2qs("&Basic colors"),
|
|
|
|
__tr2qs("&Custom colors"),
|
|
|
|
__tr2qs("&Red"),
|
|
|
|
__tr2qs("&Green"),
|
|
|
|
__tr2qs("Bl&ue"),
|
|
|
|
__tr2qs("&Define Custom Colors >>"),
|
|
|
|
__tr2qs("&Add to Custom Colors"),
|
|
|
|
// font dialog
|
|
|
|
__tr2qs("Select Font"),
|
|
|
|
__tr2qs("&Font"),
|
|
|
|
__tr2qs("Font st&yle"),
|
|
|
|
__tr2qs("&Size"),
|
|
|
|
__tr2qs("Sample"),
|
|
|
|
__tr2qs("Effects"),
|
|
|
|
__tr2qs("Stri&keout"),
|
|
|
|
__tr2qs("&Underline"),
|
|
|
|
__tr2qs("Scr&ipt"),
|
|
|
|
//File selector
|
|
|
|
__tr2qs("Parent Directory"),
|
|
|
|
__tr2qs("Back"),
|
|
|
|
__tr2qs("Forward"),
|
|
|
|
__tr2qs("Reload"),
|
|
|
|
__tr2qs("New Directory"),
|
|
|
|
__tr2qs("Bookmarks"),
|
|
|
|
__tr2qs("Add Bookmark"),
|
|
|
|
__tr2qs("&Edit Bookmarks"),
|
|
|
|
__tr2qs("New Bookmark Folder..."),
|
|
|
|
__tr2qs("Configure"),
|
|
|
|
__tr2qs("Sorting"),
|
|
|
|
__tr2qs("By Name"),
|
|
|
|
__tr2qs("By Date"),
|
|
|
|
__tr2qs("By Size"),
|
|
|
|
__tr2qs("Reverse"),
|
|
|
|
__tr2qs("Directories First"),
|
|
|
|
__tr2qs("Case Insensitive"),
|
|
|
|
__tr2qs("Short View"),
|
|
|
|
__tr2qs("Detailed View"),
|
|
|
|
__tr2qs("Show Hidden Files"),
|
|
|
|
__tr2qs("Show Quick Access Navigation Panel"),
|
|
|
|
__tr2qs("Show Preview"),
|
|
|
|
__tr2qs("Separate Directories"),
|
|
|
|
__tr2qs("Often used directories"),
|
|
|
|
__tr2qs("Desktop"),
|
|
|
|
__tr2qs("Home Directory"),
|
|
|
|
__tr2qs("Floppy"),
|
|
|
|
__tr2qs("Temporary Files"),
|
|
|
|
__tr2qs("Network"),
|
|
|
|
__tr2qs("New Directory..."),
|
|
|
|
__tr2qs("Delete"),
|
|
|
|
__tr2qs("Thumbnail Previews"),
|
|
|
|
__tr2qs("Large Icons"),
|
|
|
|
__tr2qs("Small Icons"),
|
|
|
|
__tr2qs("Properties..."),
|
|
|
|
__tr2qs("&Automatic Preview"),
|
|
|
|
__tr2qs("&Preview"),
|
|
|
|
__tr2qs("&Location:"),
|
|
|
|
__tr2qs("&Filter:"),
|
|
|
|
__tr2qs("All Files"),
|
|
|
|
__tr2qs("&OK"),
|
|
|
|
__tr2qs("&Cancel")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|