]> git.saurik.com Git - wxWidgets.git/blame - src/common/intl.cpp
Replaced ostream with FILE* in wxExpr.
[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"
43#include "wx/utils.h"
44
45#include <stdlib.h>
46
84c18814
VZ
47// ----------------------------------------------------------------------------
48// simple types
49// ----------------------------------------------------------------------------
50
e939abfd 51// this should *not* be wxChar, this type must have exactly 8 bits!
84c18814 52typedef unsigned char size_t8;
e939abfd
VZ
53
54#ifdef __WXMSW__
55 #if defined(__WIN16__)
56 typedef unsigned long size_t32;
57 #elif defined(__WIN32__)
58 typedef unsigned int size_t32;
59 #else
60 // Win64 will have different type sizes
61 #error "Please define a 32 bit type"
62 #endif
63#else // !Windows
64 // SIZEOF_XXX are defined by configure
65 #if defined(SIZEOF_INT) && (SIZEOF_INT == 4)
66 typedef unsigned int size_t32;
67 #elif defined(SIZEOF_LONG) && (SIZEOF_LONG == 4)
68 typedef unsigned long size_t32;
69 #else
70 // assume sizeof(int) == 4 - what else can we do
71 typedef unsigned int size_t32;
72
73 // ... but at least check it during run time
74 static class IntSizeChecker
75 {
76 public:
77 IntSizeChecker()
78 {
79 wxASSERT_MSG( sizeof(int) == 4,
80 "size_t32 is incorrectly defined!" );
81 }
82 } intsizechecker;
83 #endif
84#endif // Win/!Win
84c18814 85
c801d85f
KB
86// ----------------------------------------------------------------------------
87// constants
88// ----------------------------------------------------------------------------
89
90// magic number identifying the .mo format file
c86f1403
VZ
91const size_t32 MSGCATALOG_MAGIC = 0x950412de;
92const size_t32 MSGCATALOG_MAGIC_SW = 0xde120495;
c801d85f
KB
93
94// extension of ".mo" files
95#define MSGCATALOG_EXTENSION ".mo"
96
97// ----------------------------------------------------------------------------
1678ad78 98// global functions
c801d85f
KB
99// ----------------------------------------------------------------------------
100
101// suppress further error messages about missing translations
102// (if you don't have one catalog file, you wouldn't like to see the
103// error message for each string in it, so normally it's given only
104// once)
1678ad78 105void wxSuppressTransErrors();
c801d85f
KB
106
107// restore the logging
1678ad78 108void wxRestoreTransErrors();
c801d85f
KB
109
110// get the current state
1678ad78 111bool wxIsLoggingTransErrors();
c801d85f 112
84c18814 113static wxLocale *wxSetLocale(wxLocale *pLocale);
c801d85f
KB
114
115// ----------------------------------------------------------------------------
116// wxMsgCatalog corresponds to one disk-file message catalog.
117//
118// This is a "low-level" class and is used only by wxLocale (that's why
119// it's designed to be stored in a linked list)
120// ----------------------------------------------------------------------------
121
122class wxMsgCatalog
123{
124public:
125 // ctor & dtor
126 wxMsgCatalog();
127 ~wxMsgCatalog();
128
129 // load the catalog from disk (szDirPrefix corresponds to language)
e36e6f95 130 bool Load(const wxChar *szDirPrefix, const wxChar *szName);
c801d85f
KB
131 bool IsLoaded() const { return m_pData != NULL; }
132
133 // get name of the catalog
e36e6f95 134 const wxChar *GetName() const { return m_pszName; }
c801d85f
KB
135
136 // get the translated string: returns NULL if not found
137 const char *GetString(const char *sz) const;
138
139 // public variable pointing to the next element in a linked list (or NULL)
140 wxMsgCatalog *m_pNext;
7af89395 141
c801d85f
KB
142private:
143 // this implementation is binary compatible with GNU gettext() version 0.10
144
145 // an entry in the string table
146 struct wxMsgTableEntry
147 {
c86f1403
VZ
148 size_t32 nLen; // length of the string
149 size_t32 ofsString; // pointer to the string
c801d85f
KB
150 };
151
152 // header of a .mo file
153 struct wxMsgCatalogHeader
154 {
c86f1403 155 size_t32 magic, // offset +00: magic id
84c18814
VZ
156 revision, // +04: revision
157 numStrings; // +08: number of strings in the file
c86f1403 158 size_t32 ofsOrigTable, // +0C: start of original string table
84c18814 159 ofsTransTable; // +10: start of translated string table
c86f1403 160 size_t32 nHashSize, // +14: hash table size
84c18814 161 ofsHashTable; // +18: offset of hash table start
7af89395
VZ
162 };
163
c801d85f 164 // all data is stored here, NULL if no data loaded
c86f1403 165 size_t8 *m_pData;
7af89395 166
c801d85f 167 // data description
84c18814 168 size_t32 m_numStrings, // number of strings in this domain
c801d85f 169 m_nHashSize; // number of entries in hash table
84c18814 170 size_t32 *m_pHashTable; // pointer to hash table
c801d85f
KB
171 wxMsgTableEntry *m_pOrigTable, // pointer to original strings
172 *m_pTransTable; // translated
173
c86f1403 174 const char *StringAtOfs(wxMsgTableEntry *pTable, size_t32 index) const
c801d85f
KB
175 { return (const char *)(m_pData + Swap(pTable[index].ofsString)); }
176
177 // utility functions
178 // calculate the hash value of given string
c86f1403 179 static inline size_t32 GetHash(const char *sz);
c801d85f 180 // big<->little endian
c86f1403 181 inline size_t32 Swap(size_t32 ui) const;
c801d85f
KB
182
183 // internal state
184 bool HasHashTable() const // true if hash table is present
185 { return m_nHashSize > 2 && m_pHashTable != NULL; }
186
187 bool m_bSwapped; // wrong endianness?
188
e36e6f95 189 wxChar *m_pszName; // name of the domain
c801d85f
KB
190};
191
fd323a5e
VZ
192// ----------------------------------------------------------------------------
193// global variables
194// ----------------------------------------------------------------------------
195
196// the list of the directories to search for message catalog files
197static wxArrayString s_searchPrefixes;
198
c801d85f
KB
199// ============================================================================
200// implementation
201// ============================================================================
202
203// ----------------------------------------------------------------------------
204// wxMsgCatalog class
205// ----------------------------------------------------------------------------
206
207// calculate hash value using the so called hashpjw function by P.J. Weinberger
208// [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools]
c86f1403 209size_t32 wxMsgCatalog::GetHash(const char *sz)
c801d85f 210{
c86f1403 211 #define HASHWORDBITS 32 // the length of size_t32
c801d85f 212
c86f1403
VZ
213 size_t32 hval = 0;
214 size_t32 g;
c801d85f
KB
215 while ( *sz != '\0' ) {
216 hval <<= 4;
c86f1403
VZ
217 hval += (size_t32)*sz++;
218 g = hval & ((size_t32)0xf << (HASHWORDBITS - 4));
c801d85f
KB
219 if ( g != 0 ) {
220 hval ^= g >> (HASHWORDBITS - 8);
221 hval ^= g;
222 }
223 }
224
225 return hval;
226}
227
228// swap the 2 halves of 32 bit integer if needed
c86f1403 229size_t32 wxMsgCatalog::Swap(size_t32 ui) const
c801d85f 230{
7af89395 231 return m_bSwapped ? (ui << 24) | ((ui & 0xff00) << 8) |
c801d85f
KB
232 ((ui >> 8) & 0xff00) | (ui >> 24)
233 : ui;
234}
235
7af89395
VZ
236wxMsgCatalog::wxMsgCatalog()
237{
c801d85f
KB
238 m_pData = NULL;
239 m_pszName = NULL;
240}
241
7af89395
VZ
242wxMsgCatalog::~wxMsgCatalog()
243{
244 wxDELETEA(m_pData);
245 wxDELETEA(m_pszName);
c801d85f
KB
246}
247
fd323a5e 248// small class to suppress the translation erros until exit from current scope
c801d85f
KB
249class NoTransErr
250{
fd323a5e 251public:
c801d85f
KB
252 NoTransErr() { wxSuppressTransErrors(); }
253 ~NoTransErr() { wxRestoreTransErrors(); }
254};
7af89395 255
fd323a5e 256// return all directories to search for given prefix
e36e6f95
OK
257static wxString GetAllMsgCatalogSubdirs(const wxChar *prefix,
258 const wxChar *lang)
fd323a5e
VZ
259{
260 wxString searchPath;
261
262 // search first in prefix/fr/LC_MESSAGES, then in prefix/fr and finally in
263 // prefix (assuming the language is 'fr')
7af89395 264 searchPath << prefix << wxFILE_SEP_PATH << lang << wxFILE_SEP_PATH
e36e6f95 265 << _T("LC_MESSAGES") << wxPATH_SEP
7af89395
VZ
266 << prefix << wxFILE_SEP_PATH << lang << wxPATH_SEP
267 << prefix << wxPATH_SEP;
fd323a5e
VZ
268
269 return searchPath;
270}
271
272// construct the search path for the given language
e36e6f95 273static wxString GetFullSearchPath(const wxChar *lang)
fd323a5e
VZ
274{
275 wxString searchPath;
276
277 // first take the entries explicitly added by the program
278 size_t count = s_searchPrefixes.Count();
279 for ( size_t n = 0; n < count; n++ )
280 {
281 searchPath << GetAllMsgCatalogSubdirs(s_searchPrefixes[n], lang)
7af89395 282 << wxPATH_SEP;
fd323a5e
VZ
283 }
284
285 // then take the current directory
286 // FIXME it should be the directory of the executable
e36e6f95 287 searchPath << GetAllMsgCatalogSubdirs(_T("."), lang) << wxPATH_SEP;
fd323a5e
VZ
288
289 // and finally add some standard ones
290 searchPath
e36e6f95
OK
291 << GetAllMsgCatalogSubdirs(_T("/usr/share/locale"), lang) << wxPATH_SEP
292 << GetAllMsgCatalogSubdirs(_T("/usr/lib/locale"), lang) << wxPATH_SEP
293 << GetAllMsgCatalogSubdirs(_T("/usr/local/share/locale"), lang);
fd323a5e
VZ
294
295 return searchPath;
296}
297
c801d85f 298// open disk file and read in it's contents
e36e6f95 299bool wxMsgCatalog::Load(const wxChar *szDirPrefix, const wxChar *szName)
c801d85f 300{
fd323a5e
VZ
301 // FIXME VZ: I forgot the exact meaning of LC_PATH - anyone to remind me?
302#if 0
e36e6f95 303 const wxChar *pszLcPath = wxGetenv("LC_PATH");
c801d85f 304 if ( pszLcPath != NULL )
fd323a5e
VZ
305 strPath += pszLcPath + wxString(szDirPrefix) + MSG_PATH;
306#endif // 0
7af89395 307
fd323a5e 308 wxString searchPath = GetFullSearchPath(szDirPrefix);
e36e6f95 309 const wxChar *sublocale = wxStrchr(szDirPrefix, _T('_'));
fd323a5e
VZ
310 if ( sublocale )
311 {
312 // also add just base locale name: for things like "fr_BE" (belgium
313 // french) we should use "fr" if no belgium specific message catalogs
314 // exist
315 searchPath << GetFullSearchPath(wxString(szDirPrefix).
316 Left((size_t)(sublocale - szDirPrefix)))
7af89395 317 << wxPATH_SEP;
fd323a5e 318 }
7af89395 319
c801d85f
KB
320 wxString strFile = szName;
321 strFile += MSGCATALOG_EXTENSION;
322
323 // don't give translation errors here because the wxstd catalog might
324 // not yet be loaded (and it's normal)
325 //
326 // (we're using an object because we have several return paths)
327 NoTransErr noTransErr;
328
1a5a8367 329 wxLogVerbose(_("looking for catalog '%s' in path '%s'."),
fd323a5e 330 szName, searchPath.c_str());
c801d85f
KB
331
332 wxString strFullName;
fd323a5e 333 if ( !wxFindFileInPath(&strFullName, searchPath, strFile) ) {
1a5a8367 334 wxLogWarning(_("catalog file for domain '%s' not found."), szName);
c801d85f
KB
335 return FALSE;
336 }
337
338 // open file
1a5a8367 339 wxLogVerbose(_("using catalog '%s' from '%s'."),
1678ad78 340 szName, strFullName.c_str());
7af89395 341
c801d85f
KB
342 wxFile fileMsg(strFullName);
343 if ( !fileMsg.IsOpened() )
1678ad78 344 return FALSE;
c801d85f
KB
345
346 // get the file size
1678ad78
GL
347 off_t nSize = fileMsg.Length();
348 if ( nSize == wxInvalidOffset )
349 return FALSE;
c801d85f
KB
350
351 // read the whole file in memory
c86f1403 352 m_pData = new size_t8[nSize];
c801d85f 353 if ( fileMsg.Read(m_pData, nSize) != nSize ) {
a3622daa 354 wxDELETEA(m_pData);
1678ad78 355 return FALSE;
c801d85f 356 }
7af89395 357
c801d85f 358 // examine header
1678ad78 359 bool bValid = (size_t)nSize > sizeof(wxMsgCatalogHeader);
7af89395 360
fd3f686c 361 wxMsgCatalogHeader *pHeader = (wxMsgCatalogHeader *)m_pData;
c801d85f 362 if ( bValid ) {
c801d85f
KB
363 // we'll have to swap all the integers if it's true
364 m_bSwapped = pHeader->magic == MSGCATALOG_MAGIC_SW;
365
366 // check the magic number
367 bValid = m_bSwapped || pHeader->magic == MSGCATALOG_MAGIC;
368 }
7af89395 369
c801d85f
KB
370 if ( !bValid ) {
371 // it's either too short or has incorrect magic number
1a5a8367 372 wxLogWarning(_("'%s' is not a valid message catalog."), strFullName.c_str());
7af89395 373
a3622daa 374 wxDELETEA(m_pData);
c801d85f
KB
375 return FALSE;
376 }
7af89395 377
c801d85f
KB
378 // initialize
379 m_numStrings = Swap(pHeader->numStrings);
7af89395 380 m_pOrigTable = (wxMsgTableEntry *)(m_pData +
1678ad78 381 Swap(pHeader->ofsOrigTable));
7af89395 382 m_pTransTable = (wxMsgTableEntry *)(m_pData +
1678ad78 383 Swap(pHeader->ofsTransTable));
c801d85f
KB
384
385 m_nHashSize = Swap(pHeader->nHashSize);
c86f1403 386 m_pHashTable = (size_t32 *)(m_pData + Swap(pHeader->ofsHashTable));
c801d85f 387
e36e6f95
OK
388 m_pszName = new wxChar[wxStrlen(szName) + 1];
389 wxStrcpy(m_pszName, szName);
c801d85f
KB
390
391 // everything is fine
392 return TRUE;
393}
394
395// search for a string
396const char *wxMsgCatalog::GetString(const char *szOrig) const
397{
398 if ( szOrig == NULL )
399 return NULL;
400
401 if ( HasHashTable() ) { // use hash table for lookup if possible
7af89395 402 size_t32 nHashVal = GetHash(szOrig);
c86f1403 403 size_t32 nIndex = nHashVal % m_nHashSize;
c801d85f 404
c86f1403 405 size_t32 nIncr = 1 + (nHashVal % (m_nHashSize - 2));
7af89395 406
c801d85f 407 while ( TRUE ) {
c86f1403 408 size_t32 nStr = Swap(m_pHashTable[nIndex]);
c801d85f
KB
409 if ( nStr == 0 )
410 return NULL;
7af89395 411
c801d85f
KB
412 if ( strcmp(szOrig, StringAtOfs(m_pOrigTable, nStr - 1)) == 0 )
413 return StringAtOfs(m_pTransTable, nStr - 1);
414
415 if ( nIndex >= m_nHashSize - nIncr)
416 nIndex -= m_nHashSize - nIncr;
417 else
418 nIndex += nIncr;
419 }
420 }
421 else { // no hash table: use default binary search
c86f1403 422 size_t32 bottom = 0,
c801d85f
KB
423 top = m_numStrings,
424 current;
425 while ( bottom < top ) {
426 current = (bottom + top) / 2;
427 int res = strcmp(szOrig, StringAtOfs(m_pOrigTable, current));
428 if ( res < 0 )
429 top = current;
430 else if ( res > 0 )
431 bottom = current + 1;
432 else // found!
433 return StringAtOfs(m_pTransTable, current);
434 }
435 }
436
437 // not found
438 return NULL;
439}
440
441// ----------------------------------------------------------------------------
442// wxLocale
443// ----------------------------------------------------------------------------
444
23fcecf7 445wxLocale::wxLocale()
c801d85f 446{
23fcecf7
VZ
447 m_pszOldLocale = NULL;
448 m_pMsgCat = NULL;
449}
450
451// NB: this function has (desired) side effect of changing current locale
e36e6f95
OK
452bool wxLocale::Init(const wxChar *szName,
453 const wxChar *szShort,
454 const wxChar *szLocale,
23fcecf7
VZ
455 bool bLoadDefault)
456{
457 m_strLocale = szName;
458 m_strShort = szShort;
459
c801d85f
KB
460 // change current locale (default: same as long name)
461 if ( szLocale == NULL )
462 szLocale = szName;
e36e6f95 463 m_pszOldLocale = wxSetlocale(LC_ALL, szLocale);
c801d85f 464 if ( m_pszOldLocale == NULL )
1a5a8367 465 wxLogError(_("locale '%s' can not be set."), szLocale);
c801d85f
KB
466
467 // the short name will be used to look for catalog files as well,
468 // so we need something here
469 if ( m_strShort.IsEmpty() ) {
fd323a5e
VZ
470 // FIXME I don't know how these 2 letter abbreviations are formed,
471 // this wild guess is surely wrong
0fb67cd1 472 m_strShort = tolower(szLocale[0]) + tolower(szLocale[1]);
c801d85f 473 }
7af89395 474
c801d85f 475 // save the old locale to be able to restore it later
7af89395
VZ
476 m_pOldLocale = wxSetLocale(this);
477
c801d85f
KB
478 // load the default catalog with wxWindows standard messages
479 m_pMsgCat = NULL;
23fcecf7 480 bool bOk = TRUE;
c801d85f 481 if ( bLoadDefault )
e36e6f95 482 bOk = AddCatalog(_T("wxstd"));
23fcecf7
VZ
483
484 return bOk;
c801d85f
KB
485}
486
fd323a5e
VZ
487void wxLocale::AddCatalogLookupPathPrefix(const wxString& prefix)
488{
3c67202d 489 if ( s_searchPrefixes.Index(prefix) == wxNOT_FOUND )
fd323a5e
VZ
490 {
491 s_searchPrefixes.Add(prefix);
492 }
493 //else: already have it
494}
495
c801d85f
KB
496// clean up
497wxLocale::~wxLocale()
498{
fd323a5e
VZ
499 // free memory
500 wxMsgCatalog *pTmpCat;
501 while ( m_pMsgCat != NULL ) {
502 pTmpCat = m_pMsgCat;
503 m_pMsgCat = m_pMsgCat->m_pNext;
504 delete pTmpCat;
505 }
506
507 // restore old locale
508 wxSetLocale(m_pOldLocale);
e36e6f95 509 wxSetlocale(LC_ALL, m_pszOldLocale);
c801d85f
KB
510}
511
512// get the translation of given string in current locale
e36e6f95
OK
513const wxMB2WXbuf wxLocale::GetString(const wxChar *szOrigString,
514 const wxChar *szDomain) const
c801d85f 515{
e36e6f95 516 if ( wxIsEmpty(szOrigString) )
dd0e574a 517 return szDomain;
c801d85f
KB
518
519 const char *pszTrans = NULL;
dcf924a3 520 const wxWX2MBbuf szOrgString = wxConvCurrent->cWX2MB(szOrigString);
c801d85f
KB
521
522 wxMsgCatalog *pMsgCat;
523 if ( szDomain != NULL ) {
524 pMsgCat = FindCatalog(szDomain);
7af89395 525
c801d85f
KB
526 // does the catalog exist?
527 if ( pMsgCat != NULL )
e36e6f95 528 pszTrans = pMsgCat->GetString(szOrgString);
c801d85f
KB
529 }
530 else {
531 // search in all domains
532 for ( pMsgCat = m_pMsgCat; pMsgCat != NULL; pMsgCat = pMsgCat->m_pNext ) {
e36e6f95 533 pszTrans = pMsgCat->GetString(szOrgString);
c801d85f
KB
534 if ( pszTrans != NULL ) // take the first found
535 break;
536 }
537 }
538
539 if ( pszTrans == NULL ) {
540 if ( wxIsLoggingTransErrors() ) {
fd323a5e
VZ
541 // suppress further error messages if we're not debugging: this avoids
542 // flooding the user with messages about each and every missing string if,
543 // for example, a whole catalog file is missing.
544
545 // do it before calling LogWarning to prevent infinite recursion!
546#ifdef __WXDEBUG__
547 NoTransErr noTransErr;
548#else // !debug
c801d85f 549 wxSuppressTransErrors();
fd323a5e 550#endif // debug/!debug
7af89395 551
c801d85f 552 if ( szDomain != NULL )
fd323a5e 553 {
1a5a8367 554 wxLogWarning(_("string '%s' not found in domain '%s' for locale '%s'."),
fd323a5e
VZ
555 szOrigString, szDomain, m_strLocale.c_str());
556 }
c801d85f 557 else
fd323a5e 558 {
1a5a8367 559 wxLogWarning(_("string '%s' not found in locale '%s'."),
fd323a5e
VZ
560 szOrigString, m_strLocale.c_str());
561 }
c801d85f
KB
562 }
563
e36e6f95 564 return (wxMB2WXbuf)(szOrigString);
c801d85f
KB
565 }
566 else
dcf924a3 567 return (wxMB2WXbuf)(wxConvCurrent->cMB2WX(pszTrans));
c801d85f
KB
568}
569
570// find catalog by name in a linked list, return NULL if !found
e36e6f95 571wxMsgCatalog *wxLocale::FindCatalog(const wxChar *szDomain) const
c801d85f
KB
572{
573// linear search in the linked list
574 wxMsgCatalog *pMsgCat;
575 for ( pMsgCat = m_pMsgCat; pMsgCat != NULL; pMsgCat = pMsgCat->m_pNext ) {
e36e6f95 576 if ( wxStricmp(pMsgCat->GetName(), szDomain) == 0 )
c801d85f
KB
577 return pMsgCat;
578 }
7af89395 579
c801d85f
KB
580 return NULL;
581}
582
583// check if the given catalog is loaded
e36e6f95 584bool wxLocale::IsLoaded(const wxChar *szDomain) const
c801d85f
KB
585{
586 return FindCatalog(szDomain) != NULL;
587}
588
589// add a catalog to our linked list
e36e6f95 590bool wxLocale::AddCatalog(const wxChar *szDomain)
c801d85f
KB
591{
592 wxMsgCatalog *pMsgCat = new wxMsgCatalog;
7af89395 593
c801d85f
KB
594 if ( pMsgCat->Load(m_strShort, szDomain) ) {
595 // add it to the head of the list so that in GetString it will
596 // be searched before the catalogs added earlier
597 pMsgCat->m_pNext = m_pMsgCat;
598 m_pMsgCat = pMsgCat;
7af89395 599
c801d85f
KB
600 return TRUE;
601 }
602 else {
603 // don't add it because it couldn't be loaded anyway
604 delete pMsgCat;
7af89395 605
c801d85f
KB
606 return FALSE;
607 }
608}
609
610// ----------------------------------------------------------------------------
611// global functions and variables
612// ----------------------------------------------------------------------------
613
614// translation errors logging
615// --------------------------
616
617static bool gs_bGiveTransErrors = TRUE;
618
619void wxSuppressTransErrors()
620{
621 gs_bGiveTransErrors = FALSE;
622}
623
624void wxRestoreTransErrors()
625{
626 gs_bGiveTransErrors = TRUE;
627}
628
629bool wxIsLoggingTransErrors()
630{
631 return gs_bGiveTransErrors;
632}
633
634// retrieve/change current locale
635// ------------------------------
636
637// the current locale object
84c18814 638static wxLocale *g_pLocale = NULL;
c801d85f 639
1678ad78
GL
640wxLocale *wxGetLocale()
641{
7af89395 642 return g_pLocale;
1678ad78
GL
643}
644
c801d85f
KB
645wxLocale *wxSetLocale(wxLocale *pLocale)
646{
7af89395
VZ
647 wxLocale *pOld = g_pLocale;
648 g_pLocale = pLocale;
649 return pOld;
c801d85f 650}
d427503c
VZ
651
652#endif // wxUSE_INTL
653