//============================================================================= // // 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 #include #include #include #include #include #include KVILIB_API KviMessageCatalogue * g_pMainCatalogue = nullptr; static KviTranslator * g_pTranslator = nullptr; static KviPointerHashTable * 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 * 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_.mo g_pMainCatalogue = new KviMessageCatalogue(); // the catalogue dict g_pCatalogueDict = new KviPointerHashTable; g_pCatalogueDict->setAutoDelete(true); // the smart codec dict g_pSmartCodecDict = new KviPointerHashTable; // 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); }