//=============================================================================
//
// File : KviLocale.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-2010 Szymon Stefanek (pragma at kvirc dot net)
// Copyright (C) 2011 Elvio Basello (hellvis69 at gmail dot com)
//
// 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 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 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.
//
//=============================================================================
#include "kvi_debug.h"
#include "kvi_defaults.h"
#include "KviMemory.h"
#include "KviByteOrder.h"
#define _KVI_LOCALE_CPP_
#include "KviLocale.h"
#include "KviCString.h"
#include "KviQString.h"
#include "KviEnvironment.h"
#include "KviFileUtils.h"
#include "KviFile.h"
#include "KviPointerHashTable.h"
#include "KviTranslator.h"
#include <QApplication>
#include <QByteArray>
#include <QDir>
#include <QLocale>
#include <QString>
#include <QTextCodec>
#include <QtGlobal>
KVILIB_API KviMessageCatalogue * g_pMainCatalogue = nullptr;
static KviTranslator * g_pTranslator = nullptr;
static KviPointerHashTable<const char *, KviMessageCatalogue> * g_pCatalogueDict = nullptr;
static QTextCodec * g_pUtf8TextCodec = nullptr;
static QString g_szDefaultLocalePath; // FIXME: Convert this to a search path list
//
// 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., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.
//
#define UNICODE_VALID(Char) \
((Char) < 0x110000 && (((Char)&0xFFFFF800) != 0xD800) && ((Char) < 0xFDD0 || (Char) > 0xFDEF) && ((Char)&0xFFFE) != 0xFFFE)
#define CONTINUATION_CHAR \
if((*(unsigned char *)p & 0xc0) != 0x80) /* 10xxxxxx */ \
goto error; \
val <<= 6; \
val |= (*(unsigned char *)p) & 0x3f;
static const char * fast_validate(const char * str)
{
unsigned int val = 0;
unsigned int min = 0;
const char * p;
for(p = str; *p; p++)
{
if(*(unsigned char *)p < 128)
/* done */;
else
{
const char * last;
last = p;
if((*(unsigned char *)p & 0xe0) == 0xc0) /* 110xxxxx */
{
if((*(unsigned char *)p & 0x1e) == 0)
goto error;
p++;
if((*(unsigned char *)p & 0xc0) != 0x80) /* 10xxxxxx */
goto error;
}
else
{
if((*(unsigned char *)p & 0xf0) == 0xe0) /* 1110xxxx */
{
min = (1 << 11);
val = *(unsigned char *)p & 0x0f;
goto TWO_REMAINING;
}
else if((*(unsigned char *)p & 0xf8) == 0xf0) /* 11110xxx */
{
min = (1 << 16);
val = *(unsigned char *)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 char * fast_validate_len(const char * str, int max_len)
{
unsigned int val = 0;
unsigned int min = 0;
const char * p;
for(p = str; (max_len < 0 || (p - str) < max_len) && *p; p++)
{
if(*(unsigned char *)p < 128)
/* done */;
else
{
const char * last;
last = p;
if((*(unsigned char *)p & 0xe0) == 0xc0) /* 110xxxxx */
{
if(max_len >= 0 && max_len - (p - str) < 2)
goto error;
if((*(unsigned char *)p & 0x1e) == 0)
goto error;
p++;
if((*(unsigned char *)p & 0xc0) != 0x80) /* 10xxxxxx */
goto error;
}
else
{
if((*(unsigned char *)p & 0xf0) == 0xe0) /* 1110xxxx */
{
if(max_len >= 0 && max_len - (p - str) < 3)
goto error;
min = (1 << 11);
val = *(unsigned char *)p & 0x0f;
goto TWO_REMAINING;
}
else if((*(unsigned char *)p & 0xf8) == 0xf0) /* 11110xxx */
{
if(max_len >= 0 && max_len - (p - str) < 4)
goto error;
min = (1 << 16);
val = *(unsigned char *)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, int max_len, const char ** end)
{
const char * 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 QTextCodec
{
private:
QByteArray m_szName;
QTextCodec * m_pRecvCodec;
QTextCodec * m_pSendCodec;
public:
KviSmartTextCodec(const char * szName, QTextCodec * pChildCodec, bool bSendInUtf8)
: QTextCodec()
{
Q_ASSERT(pChildCodec);
m_pRecvCodec = pChildCodec;
m_szName = szName;
if(!g_pUtf8TextCodec)
{
g_pUtf8TextCodec = QTextCodec::codecForName("UTF-8");
if(!g_pUtf8TextCodec)
{
qDebug("Can't find the global UTF-8 text codec!");
g_pUtf8TextCodec = QTextCodec::codecForLocale(); // try anything else...
if(!g_pUtf8TextCodec)
qDebug("Argh! Have no UTF-8 text codec: we're in trouble.");
}
}
m_pSendCodec = bSendInUtf8 ? g_pUtf8TextCodec : pChildCodec;
}
public:
bool ok() { return m_pRecvCodec && g_pUtf8TextCodec; };
int mibEnum() const override { return 0; };
QByteArray name() const override { return m_szName; };
protected:
QByteArray convertFromUnicode(const QChar * input, int number, ConverterState * state) const override
{
return m_pSendCodec->fromUnicode(input, number, state);
}
QString convertToUnicode(const char * chars, int len, ConverterState * state) const override
{
if(g_utf8_validate(chars, len, nullptr))
return g_pUtf8TextCodec->toUnicode(chars, len, state);
return m_pRecvCodec->toUnicode(chars, len, state);
}
};
static KviPointerHashTable<const char *, KviSmartTextCodec> * g_pSmartCodecDict = nullptr;
static const char * encoding_groups[] = {
"Unicode",
"West European",
"East European",
"Cyrillic",
"Middle Eastern",
#ifndef QT_NO_BIG_CODECS
"Chinese",
"Japanese",
"Other Asiatic",
#endif
nullptr
};
static KviLocale::EncodingDescription supported_encodings[] = {
// clang-format off
// Unicode
{ "UTF-8" , 0 , 0 , 0, "8-bit Unicode" },
// West European
{ "ISO-8859-1" , 0 , 0 , 1, "Western, Latin-1" },
{ "ISO-8859-15" , 0 , 0 , 1, "Western, Latin-1 + Euro" },
{ "IBM-850" , 0 , 0 , 1, "IBM-850" },
{ "CP-1252" , 0 , 0 , 1, "Western Codepage" },
{ "ISO-8859-14" , 0 , 0 , 1, "Celtic" },
{ "ISO-8859-7" , 0 , 0 , 1, "Greek" },
{ "CP-1253" , 0 , 0 , 1, "Greek Codepage" },
{ "ISO-8859-10" , 0 , 0 , 1, "Nordic" },
{ "ISO-8859-3" , 0 , 0 , 1, "South European" },
// East European
{ "ISO-8859-4" , 0 , 0 , 2, "Baltic, Standard" },
{ "ISO-8859-13" , 0 , 0 , 2, "Baltic" },
{ "CP-1257" , 0 , 0 , 2, "Baltic Codepage" },
{ "ISO-8859-2" , 0 , 0 , 2, "Central European" },
{ "CP-1250" , 0 , 0 , 2, "Central European Codepage" },
{ "ISO-8859-9" , 0 , 0 , 2, "Turkish, Latin-5" },
{ "CP-1254" , 0 , 0 , 2, "Turkish Codepage" },
// Cyrillic
{ "ISO-8859-5" , 0 , 0 , 3, "Cyrillic, ISO" },
{ "CP-1251" , 0 , 0 , 3, "Cyrillic Codepage" },
{ "KOI8-R" , 0 , 0 , 3, "Cyrillic, KOI" },
{ "KOI8-U" , 0 , 0 , 3, "Cyrillic/Ukrainian" },
{ "IBM-866" , 0 , 0 , 3, "IBM-866" },
// Middle Eastern
{ "ISO-8859-6" , 0 , 0 , 4, "Arabic, Standard" },
{ "CP-1256" , 0 , 0 , 4, "Arabic Codepage" },
{ "ISO-8859-8" , 0 , 0 , 4, "Hebrew, visually ordered" },
{ "ISO-8859-8-i" , 0 , 0 , 4, "Hebrew, logically ordered" },
{ "CP-1255" , 0 , 0 , 4, "Hebrew Codepage" },
// Other Asiatic
{ "TIS-620" , 0 , 0 , 7, "Thai" },
{ "CP874" , 0 , 0 , 7, "Thai Codepage" },
#ifndef QT_NO_BIG_CODECS
// Chinese
{ "Big5" , 0 , 0 , 5, "Chinese Traditional" },
{ "Big5-HKSCS" , 0 , 0 , 5, "Chinese Traditional, Hong Kong" },
{ "GB18030" , 0 , 0 , 5, "Chinese Simplified" },
// Japanese
{ "ISO-2022-JP" , 0 , 0 , 6, "Japanese (ISO-2022-JP/JIS7)" },
{ "Shift-JIS" , 0 , 0 , 6, "Japanese (Shift-JIS)" },
{ "EUC-JP" , 0 , 0 , 6, "Japanese (EUC-JP)" },
// Other Asiatic
{ "EUC-KR" , 0 , 0 , 7, "Korean" },
{ "TSCII" , 0 , 0 , 7, "Tamil" },
{ "CP-949" , 0 , 0 , 7, "Korean Codepage" },
#endif
// smart codecs that send in the local charset
// West European
{ "ISO-8859-1 [UTF-8]" , 1 , 0 , 1, "Western, Latin-1 - Unicode" },
{ "ISO-8859-15 [UTF-8]" , 1 , 0 , 1, "Western, Latin-1 + Euro - Unicode" },
{ "IBM-850 [UTF-8]" , 1 , 0 , 1, "IBM-850 - Unicode" },
{ "CP-1252 [UTF-8]" , 1 , 0 , 1, "Western Codepage - Unicode" },
{ "ISO-8859-14 [UTF-8]" , 1 , 0 , 1, "Celtic - Unicode" },
{ "ISO-8859-7 [UTF-8]" , 1 , 0 , 1, "Greek - Unicode" },
{ "CP-1253 [UTF-8]" , 1 , 0 , 1, "Greek Codepage - Unicode" },
{ "ISO-8859-10 [UTF-8]" , 1 , 0 , 1, "Nordic - Unicode" },
{ "ISO-8859-3 [UTF-8]" , 1 , 0 , 1, "South European - Unicode" },
// East European
{ "ISO-8859-4 [UTF-8]" , 1 , 0 , 2, "Baltic, Standard - Unicode" },
{ "ISO-8859-13 [UTF-8]" , 1 , 0 , 2, "Baltic - Unicode" },
{ "CP-1257 [UTF-8]" , 1 , 0 , 2, "Baltic Codepage - Unicode" },
{ "ISO-8859-2 [UTF-8]" , 1 , 0 , 2, "Central European - Unicode" },
{ "CP-1250 [UTF-8]" , 1 , 0 , 2, "Central European Codepage - Unicode" },
{ "ISO-8859-9 [UTF-8]" , 1 , 0 , 2, "Turkish, Latin-5 - Unicode" },
{ "CP-1254 [UTF-8]" , 1 , 0 , 2, "Turkish Codepage - Unicode" },
// Cyrillic
{ "ISO-8859-5 [UTF-8]" , 1 , 0 , 3, "Cyrillic, ISO - Unicode" },
{ "CP-1251 [UTF-8]" , 1 , 0 , 3, "Cyrillic Codepage - Unicode" },
{ "KOI8-R [UTF-8]" , 1 , 0 , 3, "Cyrillic, KOI - Unicode" },
{ "KOI8-U [UTF-8]" , 1 , 0 , 3, "Cyrillic/Ukrainian - Unicode" },
{ "IBM-866 [UTF-8]" , 1 , 0 , 3, "IBM-866 - Unicode" },
// Middle Eastern
{ "ISO-8859-6 [UTF-8]" , 1 , 0 , 4, "Arabic, Standard - Unicode" },
{ "CP-1256 [UTF-8]" , 1 , 0 , 4, "Arabic Codepage - Unicode" },
{ "ISO-8859-8 [UTF-8]" , 1 , 0 , 4, "Hebrew, visually ordered - Unicode" },
{ "ISO-8859-8-i [UTF-8]" , 1 , 0 , 4, "Hebrew, logically ordered - Unicode" },
{ "CP-1255 [UTF-8]" , 1 , 0 , 4, "Hebrew Codepage - Unicode" },
// Other Asiatic
{ "TIS-620 [UTF-8]" , 1 , 0 , 7, "Thai - Unicode" },
{ "CP874 [UTF-8]" , 1 , 0 , 7, "Thai Codepage - Unicode" },
#ifndef QT_NO_BIG_CODECS
// Chinese
{ "Big5 [UTF-8]" , 1 , 0 , 5, "Chinese Traditional - Unicode" },
{ "Big5-HKSCS [UTF-8]" , 1 , 0 , 5, "Chinese Traditional, Hong Kong - Unicode" },
{ "GB18030 [UTF-8]" , 1 , 0 , 5, "Chinese Simplified - Unicode" },
// Japanese
{ "ISO-2022-JP [UTF-8]" , 1 , 0 , 6, "Japanese (ISO-2022-JP/JIS7) - Unicode" },
{ "Shift-JIS [UTF-8]" , 1 , 0 , 6, "Japanese (Shift-JIS) - Unicode" },
{ "EUC-JP [UTF-8]" , 1 , 0 , 6, "Japanese (EUC-JP) - Unicode" },
// Other Asiatic
{ "EUC-KR [UTF-8]" , 1 , 0 , 7, "Korean - Unicode" },
{ "TSCII [UTF-8]" , 1 , 0 , 7, "Tamil - Unicode" },
{ "CP-949 [UTF-8]" , 1 , 0 , 7, "Korean Codepage - Unicode" },
#endif
// smart codecs that send in utf8
// West European
{ "UTF-8 [ISO-8859-1]" , 1 , 1 , 1, "Unicode - Western, Latin-1" },
{ "UTF-8 [ISO-8859-15]" , 1 , 1 , 1, "Unicode - Western, Latin-1 + Euro" },
{ "UTF-8 [IBM-850]" , 1 , 1 , 1, "Unicode - IBM-850" },
{ "UTF-8 [CP-1252]" , 1 , 1 , 1, "Unicode - Western Codepage" },
{ "UTF-8 [ISO-8859-14]" , 1 , 1 , 1, "Unicode - Celtic" },
{ "UTF-8 [ISO-8859-7]" , 1 , 1 , 1, "Unicode - Greek" },
{ "UTF-8 [CP-1253]" , 1 , 1 , 1, "Unicode - Greek Codepage" },
{ "UTF-8 [ISO-8859-10]" , 1 , 1 , 1, "Unicode - Nordic" },
{ "UTF-8 [ISO-8859-3]" , 1 , 1 , 1, "Unicode - South European" },
// East European
{ "UTF-8 [ISO-8859-4]" , 1 , 1 , 2, "Unicode - Baltic, Standard" },
{ "UTF-8 [ISO-8859-13]" , 1 , 1 , 2, "Unicode - Baltic" },
{ "UTF-8 [CP-1257]" , 1 , 1 , 2, "Unicode - Baltic Codepage" },
{ "UTF-8 [ISO-8859-2]" , 1 , 1 , 2, "Unicode - Central European" },
{ "UTF-8 [CP-1250]" , 1 , 1 , 2, "Unicode - Central European Codepage" },
{ "UTF-8 [ISO-8859-9]" , 1 , 1 , 2, "Unicode - Turkish, Latin-5" },
{ "UTF-8 [CP-1254]" , 1 , 1 , 2, "Unicode - Turkish Codepage" },
// Cyrillic
{ "UTF-8 [ISO-8859-5]" , 1 , 1 , 3, "Unicode - Cyrillic, ISO" },
{ "UTF-8 [CP-1251]" , 1 , 1 , 3, "Unicode - Cyrillic Codepage" },
{ "UTF-8 [KOI8-R]" , 1 , 1 , 3, "Unicode - Cyrillic, KOI" },
{ "UTF-8 [KOI8-U]" , 1 , 1 , 3, "Unicode - Cyrillic/Ukrainian" },
{ "UTF-8 [IBM-866]" , 1 , 1 , 3, "Unicode - IBM-866" },
// Middle Eastern
{ "UTF-8 [ISO-8859-6]" , 1 , 1 , 4, "Unicode - Arabic, Standard" },
{ "UTF-8 [CP-1256]" , 1 , 1 , 4, "Unicode - Arabic Codepage" },
{ "UTF-8 [ISO-8859-8]" , 1 , 1 , 4, "Unicode - Hebrew, visually ordered" },
{ "UTF-8 [ISO-8859-8-i]" , 1 , 1 , 4, "Unicode - Hebrew, logically ordered" },
{ "UTF-8 [CP-1255]" , 1 , 1 , 4, "Unicode - Hebrew Codepage" },
// Other Asiatic
{ "UTF-8 [TIS-620]" , 1 , 1 , 7, "Unicode - Thai" },
{ "UTF-8 [CP874]" , 1 , 1 , 7, "Unicode - Thai Codepage" },
#ifndef QT_NO_BIG_CODECS
// Chinese
{ "UTF-8 [Big5]" , 1 , 1 , 5, "Unicode - Chinese Traditional" },
{ "UTF-8 [Big5-HKSCS]" , 1 , 1 , 5, "Unicode - Chinese Traditional, Hong Kong" },
{ "UTF-8 [GB18030]" , 1 , 1 , 5, "Unicode - Chinese Simplified" },
// Japanese
{ "UTF-8 [ISO-2022-JP]" , 1 , 1 , 6, "Unicode - Japanese (ISO-2022-JP/JIS7)" },
{ "UTF-8 [Shift-JIS]" , 1 , 1 , 6, "Unicode - Japanese (Shift-JIS)" },
{ "UTF-8 [EUC-JP]" , 1 , 1 , 6, "Unicode - Japanese (EUC-JP)" },
// Other Asiatic
{ "UTF-8 [EUC-KR]" , 1 , 1 , 7, "Unicode - Korean" },
{ "UTF-8 [TSCII]" , 1 , 1 , 7, "Unicode - Tamil" },
{ "UTF-8 [CP-949]" , 1 , 1 , 7, "Unicode - Korean Codepage" },
#endif
{ nullptr , 0 , 0 , 0 , nullptr }
// clang-format on
};
KviLocale * KviLocale::m_pSelf = nullptr;
QString KviLocale::g_szLang = "";
KviLocale::KviLocale(QApplication * pApp, const QString & szLocaleDir, const QString & szForceLocaleDir)
{
m_pApp = pApp;
// first of all try to find out the current locale
QString szLangFile = QString("%1/%2").arg(szForceLocaleDir, KVI_FORCE_LOCALE_FILE_NAME);
if(KviFileUtils::fileExists(szLangFile))
KviFileUtils::readFile(szLangFile, g_szLang);
if(g_szLang.isEmpty())
g_szLang = KviEnvironment::getVariable("KVIRC_LANG");
#ifdef COMPILE_KDE_SUPPORT
if(g_szLang.isEmpty())
g_szLang = KviEnvironment::getVariable("KDE_LANG");
#endif //COMPILE_KDE_SUPPORT
if(g_szLang.isEmpty())
g_szLang = KviEnvironment::getVariable("LC_MESSAGES");
if(g_szLang.isEmpty())
g_szLang = KviEnvironment::getVariable("LANG");
if(g_szLang.isEmpty())
g_szLang = QLocale::system().name();
if(g_szLang.isEmpty())
g_szLang = "en";
g_szLang = g_szLang.trimmed();
g_szDefaultLocalePath = szLocaleDir;
// 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 Qt docs explicitly state that we shouldn't delete
// the codecs by ourselves...
g_pSmartCodecDict->setAutoDelete(false);
if(!g_szLang.isEmpty())
{
// ensure Qt will use the right locale when formatting dates, numbers, ..
// Note: Qt will use the system() locale anyway, we need to construct an empty QLocale()
// to get it use our specified locale.
QLocale::setDefault(QLocale(g_szLang));
QString szBuffer;
if(findCatalogue(szBuffer, "kvirc", szLocaleDir))
{
g_pMainCatalogue->load(szBuffer);
g_pTranslator = new KviTranslator(m_pApp);
m_pApp->installTranslator(g_pTranslator);
}
else
{
KviCString 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!!!
qDebug("Can't find the catalogue for locale \"%s\" (%s)", g_szLang.toUtf8().data(), szTmp.ptr());
qDebug("There is no such translation or the $LANG variable was incorrectly set");
qDebug("You can use $KVIRC_LANG to override the catalogue name");
qDebug("For example you can set KVIRC_LANG to it_IT to force usage of the it.mo catalogue");
}
}
}
}
KviLocale::~KviLocale()
{
delete g_pMainCatalogue;
delete g_pCatalogueDict;
delete g_pSmartCodecDict;
g_pMainCatalogue = nullptr;
g_pCatalogueDict = nullptr;
g_pSmartCodecDict = nullptr;
if(g_pTranslator)
{
m_pApp->removeTranslator(g_pTranslator);
delete g_pTranslator;
g_pTranslator = nullptr;
}
}
void KviLocale::init(QApplication * pApp, const QString & szLocaleDir, const QString & szForceLocaleDir)
{
if(!m_pSelf)
{
m_pSelf = new KviLocale(pApp, szLocaleDir, szForceLocaleDir);
}
}
void KviLocale::done()
{
if(m_pSelf) {
delete m_pSelf;
m_pSelf = nullptr;
}
}
QTextCodec * KviLocale::codecForName(const char * pcName)
{
KviCString szTmp = pcName;
bool bSendUtf8;
int iIdx = szTmp.findFirstIdx('[');
if(iIdx != -1)
{
// Might be a composite codec: either UTF-8 [child codec] or child codec [UTF-8]
KviSmartTextCodec * pCodec = g_pSmartCodecDict->find(pcName);
if(pCodec)
return pCodec; // got cached copy
if(kvi_strEqualCIN("UTF-8 [", pcName, 7))
{
// Likely a smart codec that sends UTF-8
szTmp.replaceAll("UTF-8 [", "");
szTmp.replaceAll("]", "");
bSendUtf8 = true;
}
else
{
// Likely a smart codec that sends child encoding ?
szTmp.cutFromFirst(' ');
bSendUtf8 = false;
}
QTextCodec * pChildCodec = QTextCodec::codecForName(szTmp.ptr());
if(pChildCodec)
{
pCodec = new KviSmartTextCodec(pcName, pChildCodec, bSendUtf8);
if(pCodec->ok())
{
g_pSmartCodecDict->replace(pcName, pCodec);
return pCodec;
}
delete pCodec;
}
else
{
// The name of the child codec was invalid: can't create such a smart codec.
// We probably screwed up the guess above related to the [ char.
// This code path is also triggered by the yircfuzzer by specifying completely invalid codec names.
}
}
return QTextCodec::codecForName(pcName);
}
bool KviLocale::findCatalogue(QString & szBuffer, const QString & szName, const QString & szLocaleDir)
{
KviCString szLocale = g_szLang;
QString szLocDir = szLocaleDir;
KviQString::ensureLastCharIs(szLocDir, KVI_PATH_SEPARATOR_CHAR);
szBuffer = QString("%1%2_%3.mo").arg(szLocDir, szName).arg(szLocale.ptr());
if(KviFileUtils::fileExists(szBuffer))
return true;
if(szLocale.findFirstIdx('.') != -1)
{
// things like en_GB.utf8
// kill them
szLocale.cutFromFirst('.');
szBuffer = QString("%1%2_%3.mo").arg(szLocDir, szName).arg(szLocale.ptr());
if(KviFileUtils::fileExists(szBuffer))
return true;
}
if(szLocale.findFirstIdx('@') != -1)
{
// things like @euro ?
// kill them
szLocale.cutFromFirst('@');
szBuffer = QString("%1%2_%3.mo").arg(szLocDir, szName).arg(szLocale.ptr());
if(KviFileUtils::fileExists(szBuffer))
return true;
}
if(szLocale.findFirstIdx('_') != -1)
{
// things like en_GB
// kill them
szLocale.cutFromFirst('_');
szBuffer = QString("%1%2_%3.mo").arg(szLocDir, szName).arg(szLocale.ptr());
if(KviFileUtils::fileExists(szBuffer))
return true;
}
// try the lower case version too
szLocale.toLower();
szBuffer = QString("%1%2_%3.mo").arg(szLocDir, szName).arg(szLocale.ptr());
if(KviFileUtils::fileExists(szBuffer))
return true;
return false;
}
KviMessageCatalogue * KviLocale::loadCatalogue(const QString & szName, const QString & szLocaleDir)
{
//qDebug("Looking up catalogue %s",szName.toUtf8().data());
QString szBuffer;
KviMessageCatalogue * pCatalogue = g_pCatalogueDict->find(szName.toUtf8().data());
if(pCatalogue)
return pCatalogue; // already loaded
if(!findCatalogue(szBuffer, szName, szLocaleDir))
return nullptr;
pCatalogue = new KviMessageCatalogue();
if(pCatalogue->load(szBuffer))
{
g_pCatalogueDict->insert(szName.toUtf8().data(), pCatalogue);
return pCatalogue;
}
delete pCatalogue;
return nullptr;
}
bool KviLocale::unloadCatalogue(const QString & szName)
{
//qDebug("Unloading catalogue: %s",szName.toUtf8().data());
return g_pCatalogueDict->remove(szName.toUtf8().data());
}
KviMessageCatalogue * KviLocale::getLoadedCatalogue(const QString & szName)
{
return g_pCatalogueDict->find(szName.toUtf8().data());
}
const char * KviLocale::encodingGroup(int iIdx)
{
if(iIdx > KVI_NUM_ENCODING_GROUPS)
return encoding_groups[KVI_NUM_ENCODING_GROUPS];
return encoding_groups[iIdx];
}
KviLocale::EncodingDescription * KviLocale::encodingDescription(int iIdx)
{
if(iIdx > KVI_NUM_ENCODINGS)
return &(supported_encodings[KVI_NUM_ENCODINGS]);
return &(supported_encodings[iIdx]);
}
const char * KviLocale::translate(const char * pcText, const char * pcContext)
{
if(!pcContext)
return g_pMainCatalogue->translate(pcText);
KviMessageCatalogue * pCatalogue = g_pCatalogueDict->find(pcContext);
if(!pCatalogue)
{
pCatalogue = loadCatalogue(QString::fromUtf8(pcContext), g_szDefaultLocalePath);
if(!pCatalogue)
{
// Fake it....
pCatalogue = new KviMessageCatalogue();
g_pCatalogueDict->insert(pcContext, pCatalogue);
}
}
return pCatalogue->translate(pcText);
}
const QString & KviLocale::translateToQString(const char * pcText, const char * pcContext)
{
if(!pcContext)
return g_pMainCatalogue->translateToQString(pcText);
KviMessageCatalogue * pCatalogue = g_pCatalogueDict->find(pcContext);
if(!pCatalogue)
{
pCatalogue = loadCatalogue(QString::fromUtf8(pcContext), g_szDefaultLocalePath);
if(!pCatalogue)
{
// Fake it....
pCatalogue = new KviMessageCatalogue();
g_pCatalogueDict->insert(pcContext, pCatalogue);
}
}
return pCatalogue->translateToQString(pcText);
}