]> git.saurik.com Git - wxWidgets.git/blame - src/common/intl.cpp
Added chapter on collection and container classes to contents
[wxWidgets.git] / src / common / intl.cpp
CommitLineData
c801d85f
KB
1/////////////////////////////////////////////////////////////////////////////
2// Name: intl.cpp
3// Purpose: Internationalization and localisation for wxWindows
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 29/01/98
7// RCS-ID: $Id$
8// Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
7af89395 9// Licence: wxWindows license
c801d85f
KB
10/////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
1678ad78 13// declaration
c801d85f
KB
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
20#ifdef __GNUG__
84c18814 21 #pragma implementation "intl.h"
c801d85f
KB
22#endif
23
24// For compilers that support precompilation, includes "wx.h".
25#include "wx/wxprec.h"
26
27#ifdef __BORLANDC__
84c18814 28 #pragma hdrstop
c801d85f
KB
29#endif
30
d427503c
VZ
31#if wxUSE_INTL
32
1678ad78
GL
33// standard headers
34#include <locale.h>
0fb67cd1 35#include <ctype.h>
7502ba29 36
1678ad78
GL
37// wxWindows
38#include "wx/defs.h"
39#include "wx/string.h"
c801d85f
KB
40#include "wx/intl.h"
41#include "wx/file.h"
42#include "wx/log.h"
ed58dbea 43#include "wx/debug.h"
c801d85f
KB
44#include "wx/utils.h"
45
46#include <stdlib.h>
47
84c18814
VZ
48// ----------------------------------------------------------------------------
49// simple types
50// ----------------------------------------------------------------------------
51
e939abfd 52// this should *not* be wxChar, this type must have exactly 8 bits!
84c18814 53typedef unsigned char size_t8;
e939abfd
VZ
54
55#ifdef __WXMSW__
56 #if defined(__WIN16__)
57 typedef unsigned long size_t32;
58 #elif defined(__WIN32__)
59 typedef unsigned int size_t32;
60 #else
61 // Win64 will have different type sizes
62 #error "Please define a 32 bit type"
63 #endif
64#else // !Windows
65 // SIZEOF_XXX are defined by configure
66 #if defined(SIZEOF_INT) && (SIZEOF_INT == 4)
67 typedef unsigned int size_t32;
68 #elif defined(SIZEOF_LONG) && (SIZEOF_LONG == 4)
69 typedef unsigned long size_t32;
70 #else
71 // assume sizeof(int) == 4 - what else can we do
72 typedef unsigned int size_t32;
73
74 // ... but at least check it during run time
75 static class IntSizeChecker
76 {
77 public:
78 IntSizeChecker()
79 {
913df6f2
DW
80 // Asserting a sizeof directly causes some compilers to
81 // issue a "using constant in a conditional expression" warning
5f170f33 82 size_t intsize = sizeof(int);
913df6f2
DW
83
84 wxASSERT_MSG( intsize == 4,
e939abfd
VZ
85 "size_t32 is incorrectly defined!" );
86 }
87 } intsizechecker;
88 #endif
89#endif // Win/!Win
84c18814 90
c801d85f
KB
91// ----------------------------------------------------------------------------
92// constants
93// ----------------------------------------------------------------------------
94
95// magic number identifying the .mo format file
c86f1403
VZ
96const size_t32 MSGCATALOG_MAGIC = 0x950412de;
97const size_t32 MSGCATALOG_MAGIC_SW = 0xde120495;
c801d85f
KB
98
99// extension of ".mo" files
5f170f33 100#define MSGCATALOG_EXTENSION _T(".mo")
c801d85f
KB
101
102// ----------------------------------------------------------------------------
1678ad78 103// global functions
c801d85f
KB
104// ----------------------------------------------------------------------------
105
f6bcfd97 106#ifdef __WXDEBUG__
c801d85f 107
f6bcfd97
BP
108// small class to suppress the translation erros until exit from current scope
109class NoTransErr
110{
111public:
112 NoTransErr() { ms_suppressCount++; }
113 ~NoTransErr() { ms_suppressCount--; }
114
115 static bool Suppress() { return ms_suppressCount > 0; }
116
117private:
118 static size_t ms_suppressCount;
119};
c801d85f 120
f6bcfd97
BP
121size_t NoTransErr::ms_suppressCount = 0;
122
123#else // !Debug
124
125class NoTransErr
126{
127public:
128 NoTransErr() { }
129 ~NoTransErr() { }
130};
131
132#endif // Debug/!Debug
c801d85f 133
84c18814 134static wxLocale *wxSetLocale(wxLocale *pLocale);
c801d85f
KB
135
136// ----------------------------------------------------------------------------
137// wxMsgCatalog corresponds to one disk-file message catalog.
138//
139// This is a "low-level" class and is used only by wxLocale (that's why
140// it's designed to be stored in a linked list)
141// ----------------------------------------------------------------------------
142
143class wxMsgCatalog
144{
145public:
146 // ctor & dtor
147 wxMsgCatalog();
148 ~wxMsgCatalog();
149
150 // load the catalog from disk (szDirPrefix corresponds to language)
afc94fa6 151 bool Load(const wxChar *szDirPrefix, const wxChar *szName, bool bConvertEncoding = FALSE);
c801d85f
KB
152 bool IsLoaded() const { return m_pData != NULL; }
153
154 // get name of the catalog
e36e6f95 155 const wxChar *GetName() const { return m_pszName; }
c801d85f
KB
156
157 // get the translated string: returns NULL if not found
158 const char *GetString(const char *sz) const;
159
160 // public variable pointing to the next element in a linked list (or NULL)
161 wxMsgCatalog *m_pNext;
7af89395 162
c801d85f
KB
163private:
164 // this implementation is binary compatible with GNU gettext() version 0.10
165
166 // an entry in the string table
167 struct wxMsgTableEntry
168 {
c86f1403
VZ
169 size_t32 nLen; // length of the string
170 size_t32 ofsString; // pointer to the string
c801d85f
KB
171 };
172
173 // header of a .mo file
174 struct wxMsgCatalogHeader
175 {
c86f1403 176 size_t32 magic, // offset +00: magic id
84c18814
VZ
177 revision, // +04: revision
178 numStrings; // +08: number of strings in the file
c86f1403 179 size_t32 ofsOrigTable, // +0C: start of original string table
84c18814 180 ofsTransTable; // +10: start of translated string table
c86f1403 181 size_t32 nHashSize, // +14: hash table size
84c18814 182 ofsHashTable; // +18: offset of hash table start
7af89395
VZ
183 };
184
c801d85f 185 // all data is stored here, NULL if no data loaded
c86f1403 186 size_t8 *m_pData;
7af89395 187
c801d85f 188 // data description
84c18814 189 size_t32 m_numStrings, // number of strings in this domain
c801d85f 190 m_nHashSize; // number of entries in hash table
84c18814 191 size_t32 *m_pHashTable; // pointer to hash table
c801d85f
KB
192 wxMsgTableEntry *m_pOrigTable, // pointer to original strings
193 *m_pTransTable; // translated
194
c86f1403 195 const char *StringAtOfs(wxMsgTableEntry *pTable, size_t32 index) const
c801d85f 196 { return (const char *)(m_pData + Swap(pTable[index].ofsString)); }
bc385ba9 197
afc94fa6
VS
198 // convert encoding to platform native one, if neccessary
199 void ConvertEncoding();
c801d85f
KB
200
201 // utility functions
202 // calculate the hash value of given string
c86f1403 203 static inline size_t32 GetHash(const char *sz);
c801d85f 204 // big<->little endian
c86f1403 205 inline size_t32 Swap(size_t32 ui) const;
c801d85f
KB
206
207 // internal state
208 bool HasHashTable() const // true if hash table is present
209 { return m_nHashSize > 2 && m_pHashTable != NULL; }
210
211 bool m_bSwapped; // wrong endianness?
212
e36e6f95 213 wxChar *m_pszName; // name of the domain
c801d85f
KB
214};
215
fd323a5e
VZ
216// ----------------------------------------------------------------------------
217// global variables
218// ----------------------------------------------------------------------------
219
220// the list of the directories to search for message catalog files
221static wxArrayString s_searchPrefixes;
222
c801d85f
KB
223// ============================================================================
224// implementation
225// ============================================================================
226
227// ----------------------------------------------------------------------------
228// wxMsgCatalog class
229// ----------------------------------------------------------------------------
230
231// calculate hash value using the so called hashpjw function by P.J. Weinberger
232// [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools]
c86f1403 233size_t32 wxMsgCatalog::GetHash(const char *sz)
c801d85f 234{
c86f1403 235 #define HASHWORDBITS 32 // the length of size_t32
c801d85f 236
c86f1403
VZ
237 size_t32 hval = 0;
238 size_t32 g;
c801d85f
KB
239 while ( *sz != '\0' ) {
240 hval <<= 4;
c86f1403
VZ
241 hval += (size_t32)*sz++;
242 g = hval & ((size_t32)0xf << (HASHWORDBITS - 4));
c801d85f
KB
243 if ( g != 0 ) {
244 hval ^= g >> (HASHWORDBITS - 8);
245 hval ^= g;
246 }
247 }
248
249 return hval;
250}
251
252// swap the 2 halves of 32 bit integer if needed
c86f1403 253size_t32 wxMsgCatalog::Swap(size_t32 ui) const
c801d85f 254{
7af89395 255 return m_bSwapped ? (ui << 24) | ((ui & 0xff00) << 8) |
c801d85f
KB
256 ((ui >> 8) & 0xff00) | (ui >> 24)
257 : ui;
258}
259
7af89395
VZ
260wxMsgCatalog::wxMsgCatalog()
261{
c801d85f
KB
262 m_pData = NULL;
263 m_pszName = NULL;
264}
265
7af89395
VZ
266wxMsgCatalog::~wxMsgCatalog()
267{
268 wxDELETEA(m_pData);
269 wxDELETEA(m_pszName);
c801d85f
KB
270}
271
fd323a5e 272// return all directories to search for given prefix
e36e6f95
OK
273static wxString GetAllMsgCatalogSubdirs(const wxChar *prefix,
274 const wxChar *lang)
fd323a5e
VZ
275{
276 wxString searchPath;
277
278 // search first in prefix/fr/LC_MESSAGES, then in prefix/fr and finally in
279 // prefix (assuming the language is 'fr')
7af89395 280 searchPath << prefix << wxFILE_SEP_PATH << lang << wxFILE_SEP_PATH
223d09f6 281 << wxT("LC_MESSAGES") << wxPATH_SEP
7af89395
VZ
282 << prefix << wxFILE_SEP_PATH << lang << wxPATH_SEP
283 << prefix << wxPATH_SEP;
fd323a5e
VZ
284
285 return searchPath;
286}
287
288// construct the search path for the given language
e36e6f95 289static wxString GetFullSearchPath(const wxChar *lang)
fd323a5e
VZ
290{
291 wxString searchPath;
292
293 // first take the entries explicitly added by the program
294 size_t count = s_searchPrefixes.Count();
295 for ( size_t n = 0; n < count; n++ )
296 {
297 searchPath << GetAllMsgCatalogSubdirs(s_searchPrefixes[n], lang)
7af89395 298 << wxPATH_SEP;
fd323a5e
VZ
299 }
300
5f170f33
VZ
301 // LC_PATH is a standard env var containing the search path for the .mo
302 // files
f6bcfd97 303 const wxChar *pszLcPath = wxGetenv(wxT("LC_PATH"));
5f170f33
VZ
304 if ( pszLcPath != NULL )
305 searchPath << GetAllMsgCatalogSubdirs(pszLcPath, lang);
306
fd323a5e
VZ
307 // then take the current directory
308 // FIXME it should be the directory of the executable
15b8c27a 309 searchPath << GetAllMsgCatalogSubdirs(wxT("."), lang);
fd323a5e
VZ
310
311 // and finally add some standard ones
312 searchPath
5f170f33
VZ
313 << GetAllMsgCatalogSubdirs(wxT("/usr/share/locale"), lang)
314 << GetAllMsgCatalogSubdirs(wxT("/usr/lib/locale"), lang)
223d09f6 315 << GetAllMsgCatalogSubdirs(wxT("/usr/local/share/locale"), lang);
fd323a5e
VZ
316
317 return searchPath;
318}
319
c801d85f 320// open disk file and read in it's contents
afc94fa6 321bool wxMsgCatalog::Load(const wxChar *szDirPrefix, const wxChar *szName0, bool bConvertEncoding)
c801d85f 322{
03443829
KB
323 /* We need to handle locales like de_AT.iso-8859-1
324 For this we first chop off the .CHARSET specifier and ignore it.
325 FIXME: UNICODE SUPPORT: must use CHARSET specifier!
326 */
327 wxString szName = szName0;
58c837a4
RR
328 if(szName.Find(wxT('.')) != -1) // contains a dot
329 szName = szName.Left(szName.Find(wxT('.')));
2ce0a6e2 330
fd323a5e 331 wxString searchPath = GetFullSearchPath(szDirPrefix);
223d09f6 332 const wxChar *sublocale = wxStrchr(szDirPrefix, wxT('_'));
fd323a5e
VZ
333 if ( sublocale )
334 {
335 // also add just base locale name: for things like "fr_BE" (belgium
336 // french) we should use "fr" if no belgium specific message catalogs
337 // exist
338 searchPath << GetFullSearchPath(wxString(szDirPrefix).
339 Left((size_t)(sublocale - szDirPrefix)))
7af89395 340 << wxPATH_SEP;
fd323a5e 341 }
7af89395 342
c801d85f
KB
343 wxString strFile = szName;
344 strFile += MSGCATALOG_EXTENSION;
345
346 // don't give translation errors here because the wxstd catalog might
347 // not yet be loaded (and it's normal)
348 //
349 // (we're using an object because we have several return paths)
ed58dbea 350
c801d85f 351 NoTransErr noTransErr;
5f170f33 352 wxLogVerbose(_("looking for catalog '%s' in path '%s'."),
742af071 353 szName.c_str(), searchPath.c_str());
c801d85f
KB
354
355 wxString strFullName;
fd323a5e 356 if ( !wxFindFileInPath(&strFullName, searchPath, strFile) ) {
ed58dbea 357 wxLogWarning(_("catalog file for domain '%s' not found."), szName.c_str());
c801d85f
KB
358 return FALSE;
359 }
360
361 // open file
1a5a8367 362 wxLogVerbose(_("using catalog '%s' from '%s'."),
ed58dbea 363 szName.c_str(), strFullName.c_str());
7af89395 364
c801d85f
KB
365 wxFile fileMsg(strFullName);
366 if ( !fileMsg.IsOpened() )
1678ad78 367 return FALSE;
c801d85f
KB
368
369 // get the file size
1678ad78
GL
370 off_t nSize = fileMsg.Length();
371 if ( nSize == wxInvalidOffset )
372 return FALSE;
c801d85f
KB
373
374 // read the whole file in memory
c86f1403 375 m_pData = new size_t8[nSize];
c801d85f 376 if ( fileMsg.Read(m_pData, nSize) != nSize ) {
a3622daa 377 wxDELETEA(m_pData);
1678ad78 378 return FALSE;
c801d85f 379 }
7af89395 380
c801d85f 381 // examine header
1678ad78 382 bool bValid = (size_t)nSize > sizeof(wxMsgCatalogHeader);
7af89395 383
fd3f686c 384 wxMsgCatalogHeader *pHeader = (wxMsgCatalogHeader *)m_pData;
c801d85f 385 if ( bValid ) {
c801d85f
KB
386 // we'll have to swap all the integers if it's true
387 m_bSwapped = pHeader->magic == MSGCATALOG_MAGIC_SW;
388
389 // check the magic number
390 bValid = m_bSwapped || pHeader->magic == MSGCATALOG_MAGIC;
391 }
7af89395 392
c801d85f
KB
393 if ( !bValid ) {
394 // it's either too short or has incorrect magic number
1a5a8367 395 wxLogWarning(_("'%s' is not a valid message catalog."), strFullName.c_str());
7af89395 396
a3622daa 397 wxDELETEA(m_pData);
c801d85f
KB
398 return FALSE;
399 }
7af89395 400
c801d85f
KB
401 // initialize
402 m_numStrings = Swap(pHeader->numStrings);
7af89395 403 m_pOrigTable = (wxMsgTableEntry *)(m_pData +
1678ad78 404 Swap(pHeader->ofsOrigTable));
7af89395 405 m_pTransTable = (wxMsgTableEntry *)(m_pData +
1678ad78 406 Swap(pHeader->ofsTransTable));
c801d85f
KB
407
408 m_nHashSize = Swap(pHeader->nHashSize);
c86f1403 409 m_pHashTable = (size_t32 *)(m_pData + Swap(pHeader->ofsHashTable));
c801d85f 410
e36e6f95
OK
411 m_pszName = new wxChar[wxStrlen(szName) + 1];
412 wxStrcpy(m_pszName, szName);
c801d85f 413
5f170f33
VZ
414 if (bConvertEncoding)
415 ConvertEncoding();
afc94fa6 416
c801d85f
KB
417 // everything is fine
418 return TRUE;
419}
420
421// search for a string
422const char *wxMsgCatalog::GetString(const char *szOrig) const
423{
424 if ( szOrig == NULL )
425 return NULL;
426
427 if ( HasHashTable() ) { // use hash table for lookup if possible
7af89395 428 size_t32 nHashVal = GetHash(szOrig);
c86f1403 429 size_t32 nIndex = nHashVal % m_nHashSize;
c801d85f 430
c86f1403 431 size_t32 nIncr = 1 + (nHashVal % (m_nHashSize - 2));
7af89395 432
2ce0a6e2
DW
433#if defined(__VISAGECPP__)
434// VA just can't stand while(1) or while(TRUE)
435 bool bOs2var = TRUE;
436 while(bOs2var) {
437#else
438 while (1) {
439#endif
c86f1403 440 size_t32 nStr = Swap(m_pHashTable[nIndex]);
c801d85f
KB
441 if ( nStr == 0 )
442 return NULL;
7af89395 443
7aa733b3 444 if ( strcmp(szOrig, StringAtOfs(m_pOrigTable, nStr - 1)) == 0 )
c801d85f
KB
445 return StringAtOfs(m_pTransTable, nStr - 1);
446
447 if ( nIndex >= m_nHashSize - nIncr)
448 nIndex -= m_nHashSize - nIncr;
449 else
450 nIndex += nIncr;
451 }
452 }
453 else { // no hash table: use default binary search
c86f1403 454 size_t32 bottom = 0,
c801d85f
KB
455 top = m_numStrings,
456 current;
457 while ( bottom < top ) {
458 current = (bottom + top) / 2;
7aa733b3 459 int res = strcmp(szOrig, StringAtOfs(m_pOrigTable, current));
c801d85f
KB
460 if ( res < 0 )
461 top = current;
462 else if ( res > 0 )
463 bottom = current + 1;
464 else // found!
465 return StringAtOfs(m_pTransTable, current);
466 }
467 }
468
469 // not found
470 return NULL;
471}
472
afc94fa6
VS
473
474#if wxUSE_GUI
475#include "wx/fontmap.h"
476#include "wx/encconv.h"
477#endif
478
479void wxMsgCatalog::ConvertEncoding()
480{
481#if wxUSE_GUI
482 wxFontEncoding enc;
483
484 // first, find encoding header:
7e949b43
VS
485 const char *hdr = StringAtOfs(m_pOrigTable, 0);
486 if (hdr == NULL) return; // not supported by this catalog, does not have non-fuzzy header
487 if (hdr[0] != 0) return; // ditto
bc385ba9 488
7e949b43
VS
489 /* we support catalogs with header (msgid "") that is _not_ marked as "#, fuzzy" (otherwise
490 the string would not be included into compiled catalog) */
491 wxString header(StringAtOfs(m_pTransTable, 0));
492 wxString charset;
46f9bb94 493 int pos = header.Find(wxT("Content-Type: text/plain; charset="));
bc385ba9
VZ
494 if (pos == wxNOT_FOUND)
495 return; // incorrectly filled Content-Type header
496 size_t n = pos + 34; /*strlen("Content-Type: text/plain; charset=")*/
46f9bb94 497 while (header[n] != wxT('\n'))
bc385ba9
VZ
498 charset << header[n++];
499
500 enc = wxTheFontMapper->CharsetToEncoding(charset, FALSE);
501 if ( enc == wxFONTENCODING_SYSTEM )
502 return; // unknown encoding
afc94fa6
VS
503
504 wxFontEncodingArray a = wxEncodingConverter::GetPlatformEquivalents(enc);
bc385ba9
VZ
505 if (a[0] == enc)
506 return; // no conversion needed, locale uses native encoding
507
508 if (a.GetCount() == 0)
509 return; // we don't know common equiv. under this platform
510
afc94fa6 511 wxEncodingConverter converter;
bc385ba9 512
afc94fa6 513 converter.Init(enc, a[0]);
bc385ba9 514 for (size_t i = 0; i < m_numStrings; i++)
afc94fa6
VS
515 converter.Convert((char*)StringAtOfs(m_pTransTable, i));
516#endif
517}
518
519
c801d85f
KB
520// ----------------------------------------------------------------------------
521// wxLocale
522// ----------------------------------------------------------------------------
523
23fcecf7 524wxLocale::wxLocale()
c801d85f 525{
23fcecf7
VZ
526 m_pszOldLocale = NULL;
527 m_pMsgCat = NULL;
528}
529
530// NB: this function has (desired) side effect of changing current locale
e36e6f95
OK
531bool wxLocale::Init(const wxChar *szName,
532 const wxChar *szShort,
533 const wxChar *szLocale,
afc94fa6
VS
534 bool bLoadDefault,
535 bool bConvertEncoding)
23fcecf7
VZ
536{
537 m_strLocale = szName;
538 m_strShort = szShort;
afc94fa6 539 m_bConvertEncoding = bConvertEncoding;
23fcecf7 540
c801d85f
KB
541 // change current locale (default: same as long name)
542 if ( szLocale == NULL )
9dea50fc
VZ
543 {
544 // the argument to setlocale()
545 szLocale = szShort;
546 }
e36e6f95 547 m_pszOldLocale = wxSetlocale(LC_ALL, szLocale);
c801d85f 548 if ( m_pszOldLocale == NULL )
1a5a8367 549 wxLogError(_("locale '%s' can not be set."), szLocale);
c801d85f
KB
550
551 // the short name will be used to look for catalog files as well,
552 // so we need something here
553 if ( m_strShort.IsEmpty() ) {
fd323a5e
VZ
554 // FIXME I don't know how these 2 letter abbreviations are formed,
555 // this wild guess is surely wrong
0fb67cd1 556 m_strShort = tolower(szLocale[0]) + tolower(szLocale[1]);
c801d85f 557 }
7af89395 558
c801d85f 559 // save the old locale to be able to restore it later
7af89395
VZ
560 m_pOldLocale = wxSetLocale(this);
561
c801d85f
KB
562 // load the default catalog with wxWindows standard messages
563 m_pMsgCat = NULL;
23fcecf7 564 bool bOk = TRUE;
c801d85f 565 if ( bLoadDefault )
223d09f6 566 bOk = AddCatalog(wxT("wxstd"));
23fcecf7
VZ
567
568 return bOk;
c801d85f
KB
569}
570
fd323a5e
VZ
571void wxLocale::AddCatalogLookupPathPrefix(const wxString& prefix)
572{
3c67202d 573 if ( s_searchPrefixes.Index(prefix) == wxNOT_FOUND )
fd323a5e
VZ
574 {
575 s_searchPrefixes.Add(prefix);
576 }
577 //else: already have it
578}
579
c801d85f
KB
580// clean up
581wxLocale::~wxLocale()
582{
fd323a5e
VZ
583 // free memory
584 wxMsgCatalog *pTmpCat;
585 while ( m_pMsgCat != NULL ) {
586 pTmpCat = m_pMsgCat;
587 m_pMsgCat = m_pMsgCat->m_pNext;
588 delete pTmpCat;
589 }
590
591 // restore old locale
592 wxSetLocale(m_pOldLocale);
e36e6f95 593 wxSetlocale(LC_ALL, m_pszOldLocale);
c801d85f
KB
594}
595
596// get the translation of given string in current locale
e36e6f95 597const wxMB2WXbuf wxLocale::GetString(const wxChar *szOrigString,
e90c1d2a 598 const wxChar *szDomain) const
c801d85f 599{
e36e6f95 600 if ( wxIsEmpty(szOrigString) )
dd0e574a 601 return szDomain;
c801d85f
KB
602
603 const char *pszTrans = NULL;
e90c1d2a 604#if wxUSE_UNICODE
dcf924a3 605 const wxWX2MBbuf szOrgString = wxConvCurrent->cWX2MB(szOrigString);
e90c1d2a
VZ
606#else // ANSI
607 #define szOrgString szOrigString
608#endif // Unicode/ANSI
c801d85f
KB
609
610 wxMsgCatalog *pMsgCat;
611 if ( szDomain != NULL ) {
612 pMsgCat = FindCatalog(szDomain);
7af89395 613
c801d85f
KB
614 // does the catalog exist?
615 if ( pMsgCat != NULL )
e36e6f95 616 pszTrans = pMsgCat->GetString(szOrgString);
c801d85f
KB
617 }
618 else {
619 // search in all domains
620 for ( pMsgCat = m_pMsgCat; pMsgCat != NULL; pMsgCat = pMsgCat->m_pNext ) {
e36e6f95 621 pszTrans = pMsgCat->GetString(szOrgString);
c801d85f
KB
622 if ( pszTrans != NULL ) // take the first found
623 break;
624 }
625 }
626
627 if ( pszTrans == NULL ) {
fd323a5e 628#ifdef __WXDEBUG__
f6bcfd97 629 if ( !NoTransErr::Suppress() ) {
fd323a5e 630 NoTransErr noTransErr;
7af89395 631
c801d85f 632 if ( szDomain != NULL )
fd323a5e 633 {
f6bcfd97 634 wxLogDebug(_T("string '%s' not found in domain '%s' for locale '%s'."),
fd323a5e
VZ
635 szOrigString, szDomain, m_strLocale.c_str());
636 }
c801d85f 637 else
fd323a5e 638 {
f6bcfd97
BP
639 wxLogDebug(_T("string '%s' not found in locale '%s'."),
640 szOrigString, m_strLocale.c_str());
fd323a5e 641 }
c801d85f 642 }
f6bcfd97 643#endif // __WXDEBUG__
c801d85f 644
e36e6f95 645 return (wxMB2WXbuf)(szOrigString);
c801d85f
KB
646 }
647 else
e90c1d2a 648 {
f6e9f05f
OK
649 return wxConvertMB2WX(pszTrans); // or preferably wxCSConv(charset).cMB2WX(pszTrans) or something,
650 // a macro similar to wxConvertMB2WX could be written for that
e90c1d2a
VZ
651 }
652
653 #undef szOrgString
c801d85f
KB
654}
655
656// find catalog by name in a linked list, return NULL if !found
e36e6f95 657wxMsgCatalog *wxLocale::FindCatalog(const wxChar *szDomain) const
c801d85f
KB
658{
659// linear search in the linked list
660 wxMsgCatalog *pMsgCat;
661 for ( pMsgCat = m_pMsgCat; pMsgCat != NULL; pMsgCat = pMsgCat->m_pNext ) {
e36e6f95 662 if ( wxStricmp(pMsgCat->GetName(), szDomain) == 0 )
c801d85f
KB
663 return pMsgCat;
664 }
7af89395 665
c801d85f
KB
666 return NULL;
667}
668
669// check if the given catalog is loaded
e36e6f95 670bool wxLocale::IsLoaded(const wxChar *szDomain) const
c801d85f
KB
671{
672 return FindCatalog(szDomain) != NULL;
673}
674
675// add a catalog to our linked list
e36e6f95 676bool wxLocale::AddCatalog(const wxChar *szDomain)
c801d85f
KB
677{
678 wxMsgCatalog *pMsgCat = new wxMsgCatalog;
7af89395 679
afc94fa6 680 if ( pMsgCat->Load(m_strShort, szDomain, m_bConvertEncoding) ) {
c801d85f
KB
681 // add it to the head of the list so that in GetString it will
682 // be searched before the catalogs added earlier
683 pMsgCat->m_pNext = m_pMsgCat;
684 m_pMsgCat = pMsgCat;
7af89395 685
c801d85f
KB
686 return TRUE;
687 }
688 else {
689 // don't add it because it couldn't be loaded anyway
690 delete pMsgCat;
7af89395 691
c801d85f
KB
692 return FALSE;
693 }
694}
695
696// ----------------------------------------------------------------------------
697// global functions and variables
698// ----------------------------------------------------------------------------
699
c801d85f
KB
700// retrieve/change current locale
701// ------------------------------
702
703// the current locale object
84c18814 704static wxLocale *g_pLocale = NULL;
c801d85f 705
1678ad78
GL
706wxLocale *wxGetLocale()
707{
7af89395 708 return g_pLocale;
1678ad78
GL
709}
710
c801d85f
KB
711wxLocale *wxSetLocale(wxLocale *pLocale)
712{
7af89395
VZ
713 wxLocale *pOld = g_pLocale;
714 g_pLocale = pLocale;
715 return pOld;
c801d85f 716}
d427503c
VZ
717
718#endif // wxUSE_INTL
719