1
//=============================================================================
3
// File : kvi_locale.cpp
4
// Creation date : Fri Mar 19 1999 19:08:41 by Szymon Stefanek
6
// This file is part of the KVirc irc client distribution
7
// Copyright (C) 1999-2008 Szymon Stefanek (pragma at kvirc dot net)
9
// This program is FREE software. You can redistribute it and/or
10
// modify it under the terms of the GNU General Public License
11
// as published by the Free Software Foundation; either version 2
12
// of the License, or (at your opinion) any later version.
14
// This program is distributed in the HOPE that it will be USEFUL,
15
// but WITHOUT ANY WARRANTY; without even the implied warranty of
16
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
// See the GNU General Public License for more details.
19
// You should have received a copy of the GNU General Public License
20
// along with this program. If not, write to the Free Software Foundation,
21
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23
//=============================================================================
27
//#define _KVI_DEBUG_CHECK_RANGE_
28
#include "kvi_debug.h"
29
#include "kvi_malloc.h"
30
#include "kvi_bswap.h"
32
#define _KVI_LOCALE_CPP_
33
#include "kvi_locale.h"
41
#include "kvi_string.h"
43
#include "kvi_fileutils.h"
47
KVILIB_API KviMessageCatalogue * g_pMainCatalogue = 0;
49
static KviStr g_szLang;
50
static KviTranslator * g_pTranslator = 0;
51
static KviPointerHashTable<const char *,KviMessageCatalogue> * g_pCatalogueDict = 0;
52
static QTextCodec * g_pUtf8TextCodec = 0;
55
/////////////////////////////////////////////////////////////////////////////////////
57
// The following code was extracted and adapted from gutf8.c
58
// from the GNU GLIB2 package.
60
// gutf8.c - Operations on UTF-8 strings.
62
// Copyright (C) 1999 Tom Tromey
63
// Copyright (C) 2000 Red Hat, Inc.
65
// This library is free software; you can redistribute it and/or
66
// modify it under the terms of the GNU Lesser General Public
67
// License as published by the Free Software Foundation; either
68
// version 2 of the License, or (at your option) any later version.
70
// This library is distributed in the hope that it will be useful,
71
// but WITHOUT ANY WARRANTY; without even the implied warranty of
72
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
73
// Lesser General Public License for more details.
75
// You should have received a copy of the GNU Lesser General Public
76
// License along with this library; if not, write to the
77
// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
78
// Boston, MA 02111-1307, USA.
80
/////////////////////////////////////////////////////////////////////////////////////
83
typedef unsigned char guchar;
84
typedef signed int gssize;
85
typedef unsigned int gunichar;
87
#define UNICODE_VALID(Char) \
88
((Char) < 0x110000 && \
89
(((Char) & 0xFFFFF800) != 0xD800) && \
90
((Char) < 0xFDD0 || (Char) > 0xFDEF) && \
91
((Char) & 0xFFFE) != 0xFFFE)
93
#define CONTINUATION_CHAR \
94
if ((*(guchar *)p & 0xc0) != 0x80) /* 10xxxxxx */ \
97
val |= (*(guchar *)p) & 0x3f;
101
fast_validate (const char *str)
107
for (p = str; *p; p++)
109
if (*(guchar *)p < 128)
116
if ((*(guchar *)p & 0xe0) == 0xc0) /* 110xxxxx */
118
if ((*(guchar *)p & 0x1e) == 0)
121
if ((*(guchar *)p & 0xc0) != 0x80) /* 10xxxxxx */
126
if ((*(guchar *)p & 0xf0) == 0xe0) /* 1110xxxx */
129
val = *(guchar *)p & 0x0f;
132
else if ((*(guchar *)p & 0xf8) == 0xf0) /* 11110xxx */
135
val = *(guchar *)p & 0x07;
147
if (val < min) goto error;
149
if (!UNICODE_VALID(val)) goto error;
163
fast_validate_len (const char *str, gssize max_len)
169
for (p = str; (max_len < 0 || (p - str) < max_len) && *p; p++)
171
if (*(guchar *)p < 128)
178
if ((*(guchar *)p & 0xe0) == 0xc0) /* 110xxxxx */
180
if (max_len >= 0 && max_len - (p - str) < 2)
183
if ((*(guchar *)p & 0x1e) == 0)
186
if ((*(guchar *)p & 0xc0) != 0x80) /* 10xxxxxx */
191
if ((*(guchar *)p & 0xf0) == 0xe0) /* 1110xxxx */
193
if (max_len >= 0 && max_len - (p - str) < 3)
197
val = *(guchar *)p & 0x0f;
200
else if ((*(guchar *)p & 0xf8) == 0xf0) /* 11110xxx */
202
if (max_len >= 0 && max_len - (p - str) < 4)
206
val = *(guchar *)p & 0x07;
219
if (val < min) goto error;
220
if (!UNICODE_VALID(val)) goto error;
233
static bool g_utf8_validate (const char *str,
240
p = fast_validate (str);
242
p = fast_validate_len (str, max_len);
246
if ((max_len >= 0 && p != str + max_len) ||
247
(max_len < 0 && *p != '\0'))
253
/////////////////////////////////////////////////////////////////////////////////////
255
/////////////////////////////////////////////////////////////////////////////////////
258
class KviSmartTextCodec : public QTextCodec
262
QTextCodec * m_pRecvCodec;
263
QTextCodec * m_pSendCodec;
265
KviSmartTextCodec(const char * szName,const char * szChildCodecName,bool bSendInUtf8)
269
if(!g_pUtf8TextCodec)
271
g_pUtf8TextCodec = QTextCodec::codecForName("UTF-8");
272
if(!g_pUtf8TextCodec)
274
debug("Can't find the global utf8 text codec!");
275
g_pUtf8TextCodec = QTextCodec::codecForLocale(); // try anything else...
278
m_pRecvCodec = QTextCodec::codecForName(szChildCodecName);
281
debug("Can't find the codec for name %s (composite codec creation)",szName);
282
m_pRecvCodec = g_pUtf8TextCodec;
285
m_pSendCodec = g_pUtf8TextCodec;
287
m_pSendCodec = m_pRecvCodec;
290
bool ok(){ return m_pRecvCodec && g_pUtf8TextCodec; };
292
virtual int mibEnum () const { return 0; };
294
virtual QByteArray name() const { return m_szName; };
296
virtual QByteArray convertFromUnicode(const QChar * input,int number,ConverterState * state) const
298
return m_pSendCodec->fromUnicode(input,number,state);
300
virtual QString convertToUnicode(const char * chars,int len,ConverterState * state) const
302
if(g_utf8_validate(chars,len,NULL))return g_pUtf8TextCodec->toUnicode(chars,len,state);
303
return m_pRecvCodec->toUnicode(chars,len,state);
307
static KviPointerHashTable<const char *,KviSmartTextCodec> * g_pSmartCodecDict = 0;
311
/////////////////////////////////////////////////////////////////////////////////////
313
// The following code was extracted and adapted from gettext.h and gettextP.h
314
// from the GNU gettext package.
316
// Internal header for GNU gettext internationalization functions.
317
// Copyright (C) 1995, 1997 Free Software Foundation, Inc.
319
// This program is free software; you can redistribute it and/or modify
320
// it under the terms of the GNU General Public License as published by
321
// the Free Software Foundation; either version 2, or (at your option)
322
// any later version.
324
// This program is distributed in the hope that it will be useful,
325
// but WITHOUT ANY WARRANTY; without even the implied warranty of
326
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
327
// GNU General Public License for more details.
329
// You should have received a copy of the GNU Library General Public
330
// License along with the GNU C Library; see the file COPYING.LIB. If not,
331
// write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
332
// Boston, MA 02111-1307, USA.
334
/////////////////////////////////////////////////////////////////////////////////////
339
#if HAVE_LIMITS_H || _LIBC
344
// The magic number of the GNU message catalog format.
345
#define KVI_LOCALE_MAGIC 0x950412de
346
#define KVI_LOCALE_MAGIC_SWAPPED 0xde120495
348
// Revision number of the currently used .mo (binary) file format.
349
#define MO_REVISION_NUMBER 0
352
// Header for binary .mo file format.
353
struct GnuMoFileHeader
357
// The revision number of the file format.
359
// The number of strings pairs.
361
// Offset of table with start offsets of original strings.
362
kvi_u32_t orig_tab_offset;
363
// Offset of table with start offsets of translation strings.
364
kvi_u32_t trans_tab_offset;
365
// Size of hashing table.
366
kvi_u32_t hash_tab_size;
367
// Offset of first hashing entry.
368
kvi_u32_t hash_tab_offset;
371
struct GnuMoStringDescriptor
373
// Length of addressed string.
375
// Offset of string in file.
379
#define KVI_SWAP_IF_NEEDED(flag,value) (flag ? kvi_swap32(value) : (value))
381
//////////////////////////////////////////////////////////////////////////////////////// End of gettext.h & gettextP.h
382
//////////////////////////////////////////////////////////////////////////////////////
386
static int somePrimeNumbers[90]=
388
257 , 521 , 769 , 1031, 1087, 1091, 1103, 1117, 1123, 1151, // Incomplete *.mo files
389
1163, 1171, 1181, 1193, 1201, 1213, 1217, 1223, 1229, 1231, // Complete *.mo files
390
1237, 1249, 1259, 1277, 1283, 1289, 1291, 1297, 1307, 1319,
391
1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1433,
392
1447, 1459, 1471, 1481, 1493, 1511, 1523, 1531, 1543, 1553,
393
1567, 1571, 1583, 1597, 1609, 1619, 1627, 1637, 1657, 1667, // Too big for KVIrc *.mo files
394
1693, 1709, 1721, 1733, 1741, 1753, 1777, 1789, 1811, 1831,
395
1907, 2069, 2111, 2221, 2309, 2441, 2531, 2617, 2731, 2837,
396
2903, 3121, 3329, 3331, 3767, 4127, 5051, 6089, 7039, 9973
399
int kvi_getFirstBiggerPrime(int number)
401
for(int i=0;i<90;i++){
402
if(somePrimeNumbers[i] >= number)return somePrimeNumbers[i];
404
return 9973; //error!
408
KviMessageCatalogue::KviMessageCatalogue()
411
m_pTextCodec = QTextCodec::codecForLocale();
413
//m_pMessages = new KviPointerHashTable<const char *,KviTranslationEntry>(1123,true,false); // dictSize, case sensitive , don't copy keys
414
m_pMessages = new KviPointerHashTable<const char *,KviTranslationEntry>(32,true,false); // dictSize, case sensitive , don't copy keys
415
m_pMessages->setAutoDelete(true);
418
KviMessageCatalogue::~KviMessageCatalogue()
424
bool KviMessageCatalogue::load(const QString& name)
426
QString szCatalogueFile(name);
428
// Try to load the header
429
KviFile f(szCatalogueFile);
430
if(!f.open(QFile::ReadOnly))
432
debug("[KviLocale]: Failed to open the messages file %s: probably doesn't exist",KviQString::toUtf8(szCatalogueFile).data());
438
if(f.read((char *)&hdr,sizeof(GnuMoFileHeader)) < (int)sizeof(GnuMoFileHeader))
440
debug("KviLocale: Failed to read header of %s",KviQString::toUtf8(szCatalogueFile).data());
445
bool bMustSwap = false;
447
if(hdr.magic != KVI_LOCALE_MAGIC)
449
if(hdr.magic == KVI_LOCALE_MAGIC_SWAPPED)
451
debug("KviLocale: Swapped magic for file %s: swapping data too",KviQString::toUtf8(szCatalogueFile).data());
454
debug("KviLocale: Bad locale magic for file %s: not a *.mo file ?",KviQString::toUtf8(szCatalogueFile).data());
460
if(KVI_SWAP_IF_NEEDED(bMustSwap,hdr.revision) != MO_REVISION_NUMBER)
462
debug("KviLocale: Invalid *.mo file revision number for file %s",KviQString::toUtf8(szCatalogueFile).data());
467
int numberOfStrings = KVI_SWAP_IF_NEEDED(bMustSwap,hdr.nstrings);
469
if(numberOfStrings <= 0)
471
debug("KviLocale: No translated messages found in file %s",KviQString::toUtf8(szCatalogueFile).data());
476
if(numberOfStrings >= 9972)
478
debug("Number of strings too big...sure that it is a KVIrc catalog file ?");
479
numberOfStrings = 9972;
485
unsigned int fSize = f.size();
486
char * buffer = (char *)kvi_malloc(fSize);
488
// FIXME: maybe read it in blocks eh ?
489
if(f.read(buffer,fSize) < (int)fSize)
491
debug("KviLocale: Error while reading the translation file %s",KviQString::toUtf8(szCatalogueFile).data());
497
// Check for broken *.mo files
498
if(fSize < (24 + (sizeof(GnuMoStringDescriptor) * numberOfStrings)))
500
debug("KviLocale: Broken translation file %s (too small for all descriptors)",KviQString::toUtf8(szCatalogueFile).data());
506
GnuMoStringDescriptor * origDescriptor = (GnuMoStringDescriptor *)(buffer + KVI_SWAP_IF_NEEDED(bMustSwap,hdr.orig_tab_offset));
507
GnuMoStringDescriptor * transDescriptor = (GnuMoStringDescriptor *)(buffer + KVI_SWAP_IF_NEEDED(bMustSwap,hdr.trans_tab_offset));
509
// Check again for broken *.mo files
510
int expectedFileSize = KVI_SWAP_IF_NEEDED(bMustSwap,transDescriptor[numberOfStrings - 1].offset) +
511
KVI_SWAP_IF_NEEDED(bMustSwap,transDescriptor[numberOfStrings - 1].length);
513
if(fSize < (unsigned int)expectedFileSize)
515
debug("KviLocale: Broken translation file %s (too small for all the message strings)",KviQString::toUtf8(szCatalogueFile).data());
521
// Ok...we can run now
523
int dictSize = kvi_getFirstBiggerPrime(numberOfStrings);
526
m_pMessages = new KviPointerHashTable<const char *,KviTranslationEntry>(dictSize,true,false); // dictSize, case sensitive , don't copy keys
527
m_pMessages->setAutoDelete(true);
531
for(int i=0;i < numberOfStrings;i++)
533
// FIXME: "Check for NULL inside strings here ?"
534
//debug("original seems to be at %u and %u byttes long",KVI_SWAP_IF_NEEDED(bMustSwap,origDescriptor[i].offset),
535
// KVI_SWAP_IF_NEEDED(bMustSwap,origDescriptor[i].length));
536
//debug("translated seems to be at %u and %u byttes long",KVI_SWAP_IF_NEEDED(bMustSwap,transDescriptor[i].offset),
537
// KVI_SWAP_IF_NEEDED(bMustSwap,transDescriptor[i].length));
539
KviTranslationEntry * e = new KviTranslationEntry(
540
(char *)(buffer + KVI_SWAP_IF_NEEDED(bMustSwap,origDescriptor[i].offset)),
541
KVI_SWAP_IF_NEEDED(bMustSwap,origDescriptor[i].length),
542
(char *)(buffer + KVI_SWAP_IF_NEEDED(bMustSwap,transDescriptor[i].offset)),
543
KVI_SWAP_IF_NEEDED(bMustSwap,transDescriptor[i].length));
545
// In some (or all?) *.mo files the first string
546
// is zero bytes long and the translated one contains
547
// information about the translation
548
if(e->m_szKey.len() == 0)
550
szHeader = e->m_szEncodedTranslation;
555
m_pMessages->insert(e->m_szKey.ptr(),e);
563
// find out the text encoding , if possible
564
if(szHeader.hasData())
566
// find "charset=*\n"
567
int idx = szHeader.findFirstIdx("charset=");
570
szHeader.cutLeft(idx + 8);
571
szHeader.cutFromFirst('\n');
573
m_pTextCodec = KviLocale::codecForName(szHeader.ptr());
576
debug("Can't find the codec for charset=%s",szHeader.ptr());
577
debug("Falling back to codecForLocale()");
578
m_pTextCodec = QTextCodec::codecForLocale();
585
debug("The message catalogue does not have a \"charset\" header");
586
debug("Assuming utf8"); // FIXME: or codecForLocale() ?
587
m_pTextCodec = QTextCodec::codecForName("UTF-8");
593
const char * KviMessageCatalogue::translate(const char *text)
595
KviTranslationEntry * aux = m_pMessages->find(text);
596
if(aux)return aux->m_szEncodedTranslation.ptr();
600
const QString & KviMessageCatalogue::translateToQString(const char *text)
602
KviTranslationEntry * aux = m_pMessages->find(text);
605
if(aux->m_pQTranslation)return *(aux->m_pQTranslation);
606
aux->m_pQTranslation = new QString(m_pTextCodec->toUnicode(aux->m_szEncodedTranslation.ptr()));
607
return *(aux->m_pQTranslation);
609
// no translation is available: let's avoid continous string decoding
610
aux = new KviTranslationEntry(text);
611
m_pMessages->insert(aux->m_szKey.ptr(),aux);
612
aux->m_pQTranslation = new QString(m_pTextCodec->toUnicode(aux->m_szEncodedTranslation.ptr()));
613
return *(aux->m_pQTranslation);
618
#ifndef QT_NO_BIG_CODECS
619
#define NUM_ENCODINGS 109
621
#define NUM_ENCODINGS 85
624
static EncodingDescription supported_encodings[]=
626
{ "UTF-8" , 0 , 0 , "8-bit Unicode" },
627
{ "ISO-8859-1" , 0 , 0 , "Western, Latin-1" },
628
{ "ISO-8859-2" , 0 , 0 , "Central European 1" },
629
{ "ISO-8859-3" , 0 , 0 , "Central European 2" },
630
{ "ISO-8859-4" , 0 , 0 , "Baltic, Standard" },
631
{ "ISO-8859-5" , 0 , 0 , "Cyrillic, ISO" },
632
{ "ISO-8859-6" , 0 , 0 , "Arabic, Standard" },
633
{ "ISO-8859-7" , 0 , 0 , "Greek" },
634
{ "ISO-8859-8" , 0 , 0 , "Hebrew, visually ordered" },
635
{ "ISO-8859-8-i" , 0 , 0 , "Hebrew, logically ordered" },
636
{ "ISO-8859-9" , 0 , 0 , "Turkish, Latin-5" },
637
{ "ISO-8859-15" , 0 , 0 , "Western, Latin-1 + Euro" },
638
{ "KOI8-R" , 0 , 0 , "Cyrillic, KOI" },
639
{ "KOI8-U" , 0 , 0 , "Ukrainian" },
640
{ "CP-1250" , 0 , 0 , "Central European 3" },
641
{ "CP-1251" , 0 , 0 , "Cyrillic, Windows" },
642
{ "CP-1252" , 0 , 0 , "Western, CP" },
643
{ "CP-1253" , 0 , 0 , "Greek, CP" },
644
{ "CP-1256" , 0 , 0 , "Arabic, CP" },
645
{ "CP-1257" , 0 , 0 , "Baltic, CP" },
646
{ "CP-1255" , 0 , 0 , "Hebrew, CP" },
647
{ "CP-1254" , 0 , 0 , "Turkish, CP" },
648
{ "TIS-620" , 0 , 0 , "Thai" },
649
#ifndef QT_NO_BIG_CODECS
650
{ "Big5" , 0 , 0 , "Chinese Traditional" },
651
{ "Big5-HKSCS" , 0 , 0 , "Chinese Traditional, Hong Kong" },
652
{ "GB18030" , 0 , 0 , "Chinese Simplified" },
653
{ "JIS7" , 0 , 0 , "Japanese (JIS7)" },
654
{ "Shift-JIS" , 0 , 0 , "Japanese (Shift-JIS)" },
655
{ "EUC-JP" , 0 , 0 , "Japanese (EUC-JP)" },
656
{ "EUC-KR" , 0 , 0 , "Korean" },
657
{ "TSCII" , 0 , 0 , "Tamil" },
659
{ "ISO-8859-10" , 0 , 0 , "ISO-8859-10" },
660
{ "ISO-8859-13" , 0 , 0 , "ISO-8859-13" },
661
{ "ISO-8859-14" , 0 , 0 , "ISO-8859-14" },
662
{ "IBM-850" , 0 , 0 , "IBM-850" },
663
{ "IBM-866" , 0 , 0 , "IBM-866" },
664
{ "CP874" , 0 , 0 , "CP874" },
666
// smart codecs that send in the local charset
667
{ "ISO-8859-1 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Western Latin-1, O: Western Latin-1" },
668
{ "ISO-8859-2 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Central European 1, O: Central European 1" },
669
{ "ISO-8859-3 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Central European 2, O: Central European 2" },
670
{ "ISO-8859-4 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Baltic, Standard, O: Baltic, Standard" },
671
{ "ISO-8859-5 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Cyrillic, ISO, O: Cyrillic, ISO" },
672
{ "ISO-8859-6 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Arabic, Standard, O: Arabic, Standard" },
673
{ "ISO-8859-7 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Greek, O: Greek" },
674
{ "ISO-8859-8 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Hebrew, visually ordered, O: Hebrew, visually ordered" },
675
{ "ISO-8859-8-i [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Hebrew, logically ordered, O: Hebrew, logically ordered" },
676
{ "ISO-8859-9 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Turkish, Latin-5, O: Turkish, Latin-5" },
677
{ "ISO-8859-15 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Western, Latin-1 + Euro, O: Western, Latin-1 + Euro" },
678
{ "KOI8-R [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Cyrillic, KOI, O: Cyrillic, KOI" },
679
{ "KOI8-U [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Ukrainian, O: Ukrainian" },
680
{ "CP-1250 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Central European 3, O: Central European 3" },
681
{ "CP-1251 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Cyrillic, Windows, O: Cyrillic, Windows" },
682
{ "CP-1252 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Western, CP, O: Western, CP" },
683
{ "CP-1253 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Greek, CP, O: Greek, CP" },
684
{ "CP-1256 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Arabic, CP, O: Arabic, CP" },
685
{ "CP-1257 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Baltic, CP, O: Baltic, CP" },
686
{ "CP-1255 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Hebrew, CP, O: Hebrew, CP" },
687
{ "CP-1254 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Turkish, CP, O: Turkish, CP" },
688
{ "TIS-620 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Thai, O: Thai" },
689
#ifndef QT_NO_BIG_CODECS
690
{ "Big5 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Chinese Traditional, O: Chinese Traditional" },
691
{ "Big5-HKSCS [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Chinese Traditional, Hong Kong, O: Chinese Traditional, Hong Kong" },
692
{ "GB18030 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Chinese Simplified, O: Chinese Simplified" },
693
{ "JIS7 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Japanese (JIS7), O: Japanese " },
694
{ "Shift-JIS [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Japanese (Shift-JIS), O: Japanese (Shift-JIS)" },
695
{ "EUC-JP [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Japanese (EUC-JP), O: Japanese (EUC-JP)" },
696
{ "EUC-KR [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Korean, O: Korean" },
697
{ "TSCII [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Tamil, O: Tamil" },
699
{ "ISO-8859-10 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / ISO-8859-10, O: ISO-8859-10" },
700
{ "ISO-8859-13 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / ISO-8859-13, O: ISO-8859-13" },
701
{ "ISO-8859-14 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / ISO-8859-14, O: ISO-8859-14" },
702
{ "IBM-850 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / IBM-850, O: IBM-850" },
703
{ "IBM-866 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / IBM-866, O: IBM-866" },
704
{ "CP874 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / CP874, O: CP874" },
706
// smart codecs that send in utf8
707
{ "UTF-8 [ISO-8859-1]" , 1 , 1 , "I: 8-bit Unicode / Western Latin-1, O: 8-bit Unicode" },
708
{ "UTF-8 [ISO-8859-2]" , 1 , 1 , "I: 8-bit Unicode / Central European 1, O: 8-bit Unicode" },
709
{ "UTF-8 [ISO-8859-3]" , 1 , 1 , "I: 8-bit Unicode / Central European 2, O: 8-bit Unicode" },
710
{ "UTF-8 [ISO-8859-4]" , 1 , 1 , "I: 8-bit Unicode / Baltic, Standard, O: 8-bit Unicode" },
712
{ "UTF-8 [ISO-8859-5]" , 1 , 1 , "I: 8-bit Unicode / Cyrillic, ISO, O: 8-bit Unicode" },
713
{ "UTF-8 [ISO-8859-6]" , 1 , 1 , "I: 8-bit Unicode / Arabic, Standard, O: 8-bit Unicode" },
714
{ "UTF-8 [ISO-8859-7]" , 1 , 1 , "I: 8-bit Unicode / Greek, O: 8-bit Unicode" },
715
{ "UTF-8 [ISO-8859-8]" , 1 , 1 , "I: 8-bit Unicode / Hebrew, visually ordered, O: 8-bit Unicode" },
717
{ "UTF-8 [ISO-8859-8-i]" , 1 , 1 , "I: 8-bit Unicode / Hebrew, logically ordered, O: 8-bit Unicode" },
718
{ "UTF-8 [ISO-8859-9]" , 1 , 1 , "I: 8-bit Unicode / Turkish, Latin-5, O: 8-bit Unicode" },
719
{ "UTF-8 [ISO-8859-15]" , 1 , 1 , "I: 8-bit Unicode / Western, Latin-1 + Euro, O: 8-bit Unicode" },
720
{ "UTF-8 [KOI8-R]" , 1 , 1 , "I: 8-bit Unicode / Cyrillic, KOI, O: 8-bit Unicode" },
722
{ "UTF-8 [KOI8-U]" , 1 , 1 , "I: 8-bit Unicode / Ukrainian, O: 8-bit Unicode" },
723
{ "UTF-8 [CP-1250]" , 1 , 1 , "I: 8-bit Unicode / Central European 3, O: 8-bit Unicode" },
724
{ "UTF-8 [CP-1251]" , 1 , 1 , "I: 8-bit Unicode / Cyrillic, Windows, O: 8-bit Unicode" },
725
{ "UTF-8 [CP-1252]" , 1 , 1 , "I: 8-bit Unicode / Western, CP, O: 8-bit Unicode" },
727
{ "UTF-8 [CP-1253]" , 1 , 1 , "I: 8-bit Unicode / Greek, CP, O: 8-bit Unicode" },
728
{ "UTF-8 [CP-1256]" , 1 , 1 , "I: 8-bit Unicode / Arabic, CP, O: 8-bit Unicode" },
729
{ "UTF-8 [CP-1257]" , 1 , 1 , "I: 8-bit Unicode / Baltic, CP, O: 8-bit Unicode" },
730
{ "UTF-8 [CP-1255]" , 1 , 1 , "I: 8-bit Unicode / Hebrew, CP, O: 8-bit Unicode" },
732
{ "UTF-8 [CP-1254]" , 1 , 1 , "I: 8-bit Unicode / Turkish, CP, O: 8-bit Unicode" },
733
{ "UTF-8 [TIS-620]" , 1 , 1 , "I: 8-bit Unicode / Thai, O: 8-bit Unicode" },
734
#ifndef QT_NO_BIG_CODECS
735
{ "UTF-8 [Big5]" , 1 , 1 , "I: 8-bit Unicode / Chinese Traditional, O: 8-bit Unicode" },
736
{ "UTF-8 [Big5-HKSCS]" , 1 , 1 , "I: 8-bit Unicode / Chinese Traditional, Hong Kong, O: 8-bit Unicode" },
738
{ "UTF-8 [GB18030]" , 1 , 1 , "I: 8-bit Unicode / Chinese Simplified, O: 8-bit Unicode" },
739
{ "UTF-8 [JIS7]" , 1 , 1 , "I: 8-bit Unicode / Japanese (JIS7), O: 8-bit Unicode" },
740
{ "UTF-8 [Shift-JIS]" , 1 , 1 , "I: 8-bit Unicode / Japanese (Shift-JIS), O: Japanese (Shift-JIS)" },
741
{ "UTF-8 [EUC-JP]" , 1 , 1 , "I: 8-bit Unicode / Japanese (EUC-JP), O: Japanese (EUC-JP)" },
743
{ "UTF-8 [EUC-KR]" , 1 , 1 , "I: 8-bit Unicode / Korean, O: 8-bit Unicode" },
744
{ "UTF-8 [TSCII]" , 1 , 1 , "I: 8-bit Unicode / Tamil, O: 8-bit Unicode" },
746
{ "UTF-8 [ISO-8859-10]" , 1 , 1 , "I: 8-bit Unicode / ISO-8859-10, O: 8-bit Unicode" },
747
{ "UTF-8 [ISO-8859-13]" , 1 , 1 , "I: 8-bit Unicode / ISO-8859-13, O: 8-bit Unicode" },
749
{ "UTF-8 [ISO-8859-14]" , 1 , 1 , "I: 8-bit Unicode / ISO-8859-14, O: 8-bit Unicode" },
750
{ "UTF-8 [IBM-850]" , 1 , 1 , "I: 8-bit Unicode / IBM-850, O: 8-bit Unicode" },
751
{ "UTF-8 [IBM-866]" , 1 , 1 , "I: 8-bit Unicode / IBM-866, O: 8-bit Unicode" },
752
{ "UTF-8 [CP874]" , 1 , 1 , "I: 8-bit Unicode / CP874, O: 8-bit Unicode" },
757
EncodingDescription * encodingDescription(int iIdx)
759
if(iIdx > NUM_ENCODINGS)return &(supported_encodings[NUM_ENCODINGS]);
760
return &(supported_encodings[iIdx]);
763
QTextCodec * codecForName(const char * szName)
765
KviStr szTmp = szName;
766
int idx = szTmp.findFirstIdx('[');
769
// composite codec: either UTF-8 [child codec] or child codec [UTF-8]
770
KviSmartTextCodec * c = g_pSmartCodecDict->find(szName);
774
if(kvi_strEqualCIN("UTF-8 [",szName,7))
776
szTmp.replaceAll("UTF-8 [","");
777
szTmp.replaceAll("]","");
778
// smart codec that sends UTF-8
779
c = new KviSmartTextCodec(szName,szTmp.ptr(),true);
781
szTmp.cutFromFirst(' ');
782
// smart codec that sends child encoding
783
c = new KviSmartTextCodec(szName,szTmp.ptr(),false);
787
g_pSmartCodecDict->replace(szName,c);
793
return QTextCodec::codecForName(szName);
796
const KviStr & localeName()
801
bool loadCatalogue(const QString &name,const QString &szLocaleDir)
803
//debug("Looking up catalogue %s",name);
804
if(g_pCatalogueDict->find(KviQString::toUtf8(name).data()))
805
return true; // already loaded
809
if(findCatalogue(szBuffer,name,szLocaleDir))
811
KviMessageCatalogue * c = new KviMessageCatalogue();
812
if(c->load(szBuffer))
814
g_pCatalogueDict->insert(KviQString::toUtf8(name).data(),c);
823
bool unloadCatalogue(const QString &name)
825
//debug("Unloading catalogue : %s",name);
826
return g_pCatalogueDict->remove(KviQString::toUtf8(name).data());
829
bool findCatalogue(QString &szBuffer,const QString& name,const QString& szLocaleDir)
831
KviStr szLocale = g_szLang;
833
QString szLocDir = szLocaleDir;
834
KviQString::ensureLastCharIs(szLocDir,KVI_PATH_SEPARATOR_CHAR);
836
KviQString::sprintf(szBuffer,"%Q%Q_%s.mo",&szLocDir,&name,szLocale.ptr());
838
if(KviFileUtils::fileExists(szBuffer))return true;
840
if(szLocale.findFirstIdx('.') != -1)
842
// things like en_GB.utf8
844
szLocale.cutFromFirst('.');
846
KviQString::sprintf(szBuffer,"%Q%Q_%s.mo",&szLocDir,&name,szLocale.ptr());
847
if(KviFileUtils::fileExists(szBuffer))return true;
850
if(szLocale.findFirstIdx('@') != -1)
852
// things like @euro ?
854
szLocale.cutFromFirst('@');
855
KviQString::sprintf(szBuffer,"%Q%Q_%s.mo",&szLocDir,&name,szLocale.ptr());
856
if(KviFileUtils::fileExists(szBuffer))return true;
859
if(szLocale.findFirstIdx('_') != -1)
863
szLocale.cutFromFirst('_');
864
KviQString::sprintf(szBuffer,"%Q%Q_%s.mo",&szLocDir,&name,szLocale.ptr());
865
if(KviFileUtils::fileExists(szBuffer))return true;
868
// try the lower case version too
870
KviQString::sprintf(szBuffer,"%Q%Q_%s.mo",&szLocDir,&name,szLocale.ptr());
871
if(KviFileUtils::fileExists(szBuffer))return true;
877
// This function attempts to determine the current locale
878
// and then load the corresponding translation file
879
// from the KVIrc locale directory
880
// Returns true if the locale was correctly set
881
// i.e. the locale is C or POSIX (no translation needed)
882
// or the locale is correctly defined and the
883
// translation map was sucesfully loaded
886
void init(QApplication * app,const QString &localeDir)
888
// first of all try to find out the current locale
890
QString szLangFile=QString("%1/.kvirc_force_locale").arg(QDir::homePath());
892
if(KviFileUtils::fileExists(szLangFile))
895
KviFileUtils::readFile(szLangFile,szTmp);
898
if(g_szLang.isEmpty())g_szLang = kvi_getenv("KVIRC_LANG");
899
if(g_szLang.isEmpty())g_szLang = kvi_getenv("LC_MESSAGES");
900
if(g_szLang.isEmpty())g_szLang = kvi_getenv("LANG");
901
if(g_szLang.isEmpty())g_szLang = QLocale::system().name();
902
if(g_szLang.isEmpty())g_szLang = "en";
905
// the main catalogue is supposed to be kvirc_<language>.mo
906
g_pMainCatalogue = new KviMessageCatalogue();
907
// the catalogue dict
908
g_pCatalogueDict = new KviPointerHashTable<const char *,KviMessageCatalogue>;
909
g_pCatalogueDict->setAutoDelete(true);
911
// the smart codec dict
912
g_pSmartCodecDict = new KviPointerHashTable<const char *,KviSmartTextCodec>;
913
// the Qt docs explicitly state that we shouldn't delete
914
// the codecs by ourselves...
915
g_pSmartCodecDict->setAutoDelete(false);
917
if(g_szLang.hasData())
920
if(findCatalogue(szBuffer,"kvirc",localeDir))
922
g_pMainCatalogue->load(szBuffer);
923
g_pTranslator = new KviTranslator(app,"kvirc_translator");
924
app->installTranslator(g_pTranslator);
926
KviStr szTmp = g_szLang;
927
szTmp.cutFromFirst('.');
928
szTmp.cutFromFirst('_');
929
szTmp.cutFromFirst('@');
931
if(!(kvi_strEqualCI(szTmp.ptr(),"en") ||
932
kvi_strEqualCI(szTmp.ptr(),"c") ||
933
kvi_strEqualCI(szTmp.ptr(),"us") ||
934
kvi_strEqualCI(szTmp.ptr(),"gb") ||
935
kvi_strEqualCI(szTmp.ptr(),"posix")))
937
// FIXME: THIS IS NO LONGER VALID!!!
938
debug("Can't find the catalogue for locale \"%s\" (%s)",g_szLang.ptr(),szTmp.ptr());
939
debug("There is no such translation or the $LANG variable was incorrectly set");
940
debug("You can use $KVIRC_LANG to override the catalogue name");
941
debug("For example you can set KVIRC_LANG to it_IT to force usage of the it.mo catalogue");
946
//g_pTextCodec = QTextCodec::codecForLocale();
947
//if(!g_pTextCodec)g_pTextCodec = QTextCodec::codecForLocale();
950
void done(QApplication * app)
952
delete g_pMainCatalogue;
953
delete g_pCatalogueDict;
954
delete g_pSmartCodecDict;
955
g_pMainCatalogue = 0;
956
g_pCatalogueDict = 0;
957
g_pSmartCodecDict = 0;
960
app->removeTranslator(g_pTranslator);
961
delete g_pTranslator;
966
KviMessageCatalogue * getLoadedCatalogue(const QString& name)
968
return g_pCatalogueDict->find(KviQString::toUtf8(name).data());
972
const char * translate(const char * text,const char * context)
976
KviMessageCatalogue * c = g_pCatalogueDict->find(context);
979
// FIXME: Should really try to load the catalogue here!
980
c = new KviMessageCatalogue();
981
g_pCatalogueDict->insert(context,c);
983
return c->translate(text);
985
return g_pMainCatalogue->translate(text);
988
const QString & translateToQString(const char * text,const char * context)
992
KviMessageCatalogue * c = g_pCatalogueDict->find(context);
995
// FIXME: Should really try to load the catalogue here!
996
c = new KviMessageCatalogue();
997
g_pCatalogueDict->insert(context,c);
999
return c->translateToQString(text);
1001
return g_pMainCatalogue->translateToQString(text);
1005
KviTranslator::KviTranslator(QObject * par,const char *)
1010
KviTranslator::~KviTranslator()
1014
QString KviTranslator::translate(const char *,const char * message,const char *) const
1016
// we ignore contexts and comments for qt translations
1017
return g_pMainCatalogue->translateToQString(message);
1022
// a fake table that will force these translations
1023
// to be included in the *.pot file
1025
static QString fake_translations_table[]=
1031
__tr2qs("Select color"),
1032
__tr2qs("&Basic colors"),
1033
__tr2qs("&Custom colors"),
1037
__tr2qs("&Define Custom Colors >>"),
1038
__tr2qs("&Add to Custom Colors"),
1040
__tr2qs("Select Font"),
1042
__tr2qs("Font st&yle"),
1046
__tr2qs("Stri&keout"),
1047
__tr2qs("&Underline"),
1050
__tr2qs("Parent Directory"),
1054
__tr2qs("New Directory"),
1055
__tr2qs("Bookmarks"),
1056
__tr2qs("Add Bookmark"),
1057
__tr2qs("&Edit Bookmarks"),
1058
__tr2qs("New Bookmark Folder..."),
1059
__tr2qs("Configure"),
1065
__tr2qs("Directories First"),
1066
__tr2qs("Case Insensitive"),
1067
__tr2qs("Short View"),
1068
__tr2qs("Detailed View"),
1069
__tr2qs("Show Hidden Files"),
1070
__tr2qs("Show Quick Access Navigation Panel"),
1071
__tr2qs("Show Preview"),
1072
__tr2qs("Separate Directories"),
1073
__tr2qs("Often used directories"),
1075
__tr2qs("Home Directory"),
1077
__tr2qs("Temporary Files"),
1079
__tr2qs("New Directory..."),
1081
__tr2qs("Thumbnail Previews"),
1082
__tr2qs("Large Icons"),
1083
__tr2qs("Small Icons"),
1084
__tr2qs("Properties..."),
1085
__tr2qs("&Automatic Preview"),
1086
__tr2qs("&Preview"),
1087
__tr2qs("&Location:"),
1088
__tr2qs("&Filter:"),
1089
__tr2qs("All Files"),