/[pcsx2_0.9.7]/trunk/3rdparty/wxWidgets/src/common/intl.cpp
ViewVC logotype

Annotation of /trunk/3rdparty/wxWidgets/src/common/intl.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 31 - (hide annotations) (download)
Tue Sep 7 03:24:11 2010 UTC (10 years, 4 months ago) by william
File size: 130899 byte(s)
committing r3113 initial commit again...
1 william 31 /////////////////////////////////////////////////////////////////////////////
2     // Name: src/common/intl.cpp
3     // Purpose: Internationalization and localisation for wxWidgets
4     // Author: Vadim Zeitlin
5     // Modified by: Michael N. Filippov <michael@idisys.iae.nsk.su>
6     // (2003/09/30 - PluralForms support)
7     // Created: 29/01/98
8     // RCS-ID: $Id: intl.cpp 57501 2008-12-22 19:50:17Z VZ $
9     // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
10     // Licence: wxWindows licence
11     /////////////////////////////////////////////////////////////////////////////
12    
13     // ============================================================================
14     // declaration
15     // ============================================================================
16    
17     // ----------------------------------------------------------------------------
18     // headers
19     // ----------------------------------------------------------------------------
20    
21     // For compilers that support precompilation, includes "wx.h".
22     #include "wx/wxprec.h"
23    
24     #ifdef __BORLANDC__
25     #pragma hdrstop
26     #endif
27    
28     #ifdef __EMX__
29     // The following define is needed by Innotek's libc to
30     // make the definition of struct localeconv available.
31     #define __INTERNAL_DEFS
32     #endif
33    
34     #if wxUSE_INTL
35    
36     #ifndef WX_PRECOMP
37     #include "wx/dynarray.h"
38     #include "wx/string.h"
39     #include "wx/intl.h"
40     #include "wx/log.h"
41     #include "wx/utils.h"
42     #include "wx/app.h"
43     #include "wx/hashmap.h"
44     #include "wx/module.h"
45     #endif // WX_PRECOMP
46    
47     #ifndef __WXWINCE__
48     #include <locale.h>
49     #endif
50    
51     // standard headers
52     #include <ctype.h>
53     #include <stdlib.h>
54     #ifdef HAVE_LANGINFO_H
55     #include <langinfo.h>
56     #endif
57    
58     #ifdef __WIN32__
59     #include "wx/msw/private.h"
60     #elif defined(__UNIX_LIKE__)
61     #include "wx/fontmap.h" // for CharsetToEncoding()
62     #endif
63    
64     #include "wx/file.h"
65     #include "wx/filename.h"
66     #include "wx/tokenzr.h"
67     #include "wx/fontmap.h"
68     #include "wx/encconv.h"
69     #include "wx/ptr_scpd.h"
70     #include "wx/apptrait.h"
71     #include "wx/stdpaths.h"
72    
73     #if defined(__WXMAC__)
74     #include "wx/mac/private.h" // includes mac headers
75     #endif
76    
77     #if defined(__DARWIN__)
78     #include "wx/mac/corefoundation/cfref.h"
79     #include <CoreFoundation/CFLocale.h>
80     #include "wx/mac/corefoundation/cfstring.h"
81     #endif
82    
83     // ----------------------------------------------------------------------------
84     // simple types
85     // ----------------------------------------------------------------------------
86    
87     // this should *not* be wxChar, this type must have exactly 8 bits!
88     typedef wxUint8 size_t8;
89     typedef wxUint32 size_t32;
90    
91     // ----------------------------------------------------------------------------
92     // constants
93     // ----------------------------------------------------------------------------
94    
95     // magic number identifying the .mo format file
96     const size_t32 MSGCATALOG_MAGIC = 0x950412de;
97     const size_t32 MSGCATALOG_MAGIC_SW = 0xde120495;
98    
99     // the constants describing the format of lang_LANG locale string
100     static const size_t LEN_LANG = 2;
101     static const size_t LEN_SUBLANG = 2;
102     static const size_t LEN_FULL = LEN_LANG + 1 + LEN_SUBLANG; // 1 for '_'
103    
104     #define TRACE_I18N _T("i18n")
105    
106     // ----------------------------------------------------------------------------
107     // global functions
108     // ----------------------------------------------------------------------------
109    
110     #ifdef __WXDEBUG__
111    
112     // small class to suppress the translation erros until exit from current scope
113     class NoTransErr
114     {
115     public:
116     NoTransErr() { ms_suppressCount++; }
117     ~NoTransErr() { ms_suppressCount--; }
118    
119     static bool Suppress() { return ms_suppressCount > 0; }
120    
121     private:
122     static size_t ms_suppressCount;
123     };
124    
125     size_t NoTransErr::ms_suppressCount = 0;
126    
127     #else // !Debug
128    
129     class NoTransErr
130     {
131     public:
132     NoTransErr() { }
133     ~NoTransErr() { }
134     };
135    
136     #endif // Debug/!Debug
137    
138     static wxLocale *wxSetLocale(wxLocale *pLocale);
139    
140     // helper functions of GetSystemLanguage()
141     #ifdef __UNIX__
142    
143     // get just the language part
144     static inline wxString ExtractLang(const wxString& langFull)
145     {
146     return langFull.Left(LEN_LANG);
147     }
148    
149     // get everything else (including the leading '_')
150     static inline wxString ExtractNotLang(const wxString& langFull)
151     {
152     return langFull.Mid(LEN_LANG);
153     }
154    
155     #endif // __UNIX__
156    
157    
158     // ----------------------------------------------------------------------------
159     // Plural forms parser
160     // ----------------------------------------------------------------------------
161    
162     /*
163     Simplified Grammar
164    
165     Expression:
166     LogicalOrExpression '?' Expression ':' Expression
167     LogicalOrExpression
168    
169     LogicalOrExpression:
170     LogicalAndExpression "||" LogicalOrExpression // to (a || b) || c
171     LogicalAndExpression
172    
173     LogicalAndExpression:
174     EqualityExpression "&&" LogicalAndExpression // to (a && b) && c
175     EqualityExpression
176    
177     EqualityExpression:
178     RelationalExpression "==" RelationalExperession
179     RelationalExpression "!=" RelationalExperession
180     RelationalExpression
181    
182     RelationalExpression:
183     MultiplicativeExpression '>' MultiplicativeExpression
184     MultiplicativeExpression '<' MultiplicativeExpression
185     MultiplicativeExpression ">=" MultiplicativeExpression
186     MultiplicativeExpression "<=" MultiplicativeExpression
187     MultiplicativeExpression
188    
189     MultiplicativeExpression:
190     PmExpression '%' PmExpression
191     PmExpression
192    
193     PmExpression:
194     N
195     Number
196     '(' Expression ')'
197     */
198    
199     class wxPluralFormsToken
200     {
201     public:
202     enum Type
203     {
204     T_ERROR, T_EOF, T_NUMBER, T_N, T_PLURAL, T_NPLURALS, T_EQUAL, T_ASSIGN,
205     T_GREATER, T_GREATER_OR_EQUAL, T_LESS, T_LESS_OR_EQUAL,
206     T_REMINDER, T_NOT_EQUAL,
207     T_LOGICAL_AND, T_LOGICAL_OR, T_QUESTION, T_COLON, T_SEMICOLON,
208     T_LEFT_BRACKET, T_RIGHT_BRACKET
209     };
210     Type type() const { return m_type; }
211     void setType(Type type) { m_type = type; }
212     // for T_NUMBER only
213     typedef int Number;
214     Number number() const { return m_number; }
215     void setNumber(Number num) { m_number = num; }
216     private:
217     Type m_type;
218     Number m_number;
219     };
220    
221    
222     class wxPluralFormsScanner
223     {
224     public:
225     wxPluralFormsScanner(const char* s);
226     const wxPluralFormsToken& token() const { return m_token; }
227     bool nextToken(); // returns false if error
228     private:
229     const char* m_s;
230     wxPluralFormsToken m_token;
231     };
232    
233     wxPluralFormsScanner::wxPluralFormsScanner(const char* s) : m_s(s)
234     {
235     nextToken();
236     }
237    
238     bool wxPluralFormsScanner::nextToken()
239     {
240     wxPluralFormsToken::Type type = wxPluralFormsToken::T_ERROR;
241     while (isspace((unsigned char) *m_s))
242     {
243     ++m_s;
244     }
245     if (*m_s == 0)
246     {
247     type = wxPluralFormsToken::T_EOF;
248     }
249     else if (isdigit((unsigned char) *m_s))
250     {
251     wxPluralFormsToken::Number number = *m_s++ - '0';
252     while (isdigit((unsigned char) *m_s))
253     {
254     number = number * 10 + (*m_s++ - '0');
255     }
256     m_token.setNumber(number);
257     type = wxPluralFormsToken::T_NUMBER;
258     }
259     else if (isalpha((unsigned char) *m_s))
260     {
261     const char* begin = m_s++;
262     while (isalnum((unsigned char) *m_s))
263     {
264     ++m_s;
265     }
266     size_t size = m_s - begin;
267     if (size == 1 && memcmp(begin, "n", size) == 0)
268     {
269     type = wxPluralFormsToken::T_N;
270     }
271     else if (size == 6 && memcmp(begin, "plural", size) == 0)
272     {
273     type = wxPluralFormsToken::T_PLURAL;
274     }
275     else if (size == 8 && memcmp(begin, "nplurals", size) == 0)
276     {
277     type = wxPluralFormsToken::T_NPLURALS;
278     }
279     }
280     else if (*m_s == '=')
281     {
282     ++m_s;
283     if (*m_s == '=')
284     {
285     ++m_s;
286     type = wxPluralFormsToken::T_EQUAL;
287     }
288     else
289     {
290     type = wxPluralFormsToken::T_ASSIGN;
291     }
292     }
293     else if (*m_s == '>')
294     {
295     ++m_s;
296     if (*m_s == '=')
297     {
298     ++m_s;
299     type = wxPluralFormsToken::T_GREATER_OR_EQUAL;
300     }
301     else
302     {
303     type = wxPluralFormsToken::T_GREATER;
304     }
305     }
306     else if (*m_s == '<')
307     {
308     ++m_s;
309     if (*m_s == '=')
310     {
311     ++m_s;
312     type = wxPluralFormsToken::T_LESS_OR_EQUAL;
313     }
314     else
315     {
316     type = wxPluralFormsToken::T_LESS;
317     }
318     }
319     else if (*m_s == '%')
320     {
321     ++m_s;
322     type = wxPluralFormsToken::T_REMINDER;
323     }
324     else if (*m_s == '!' && m_s[1] == '=')
325     {
326     m_s += 2;
327     type = wxPluralFormsToken::T_NOT_EQUAL;
328     }
329     else if (*m_s == '&' && m_s[1] == '&')
330     {
331     m_s += 2;
332     type = wxPluralFormsToken::T_LOGICAL_AND;
333     }
334     else if (*m_s == '|' && m_s[1] == '|')
335     {
336     m_s += 2;
337     type = wxPluralFormsToken::T_LOGICAL_OR;
338     }
339     else if (*m_s == '?')
340     {
341     ++m_s;
342     type = wxPluralFormsToken::T_QUESTION;
343     }
344     else if (*m_s == ':')
345     {
346     ++m_s;
347     type = wxPluralFormsToken::T_COLON;
348     } else if (*m_s == ';') {
349     ++m_s;
350     type = wxPluralFormsToken::T_SEMICOLON;
351     }
352     else if (*m_s == '(')
353     {
354     ++m_s;
355     type = wxPluralFormsToken::T_LEFT_BRACKET;
356     }
357     else if (*m_s == ')')
358     {
359     ++m_s;
360     type = wxPluralFormsToken::T_RIGHT_BRACKET;
361     }
362     m_token.setType(type);
363     return type != wxPluralFormsToken::T_ERROR;
364     }
365    
366     class wxPluralFormsNode;
367    
368     // NB: Can't use wxDEFINE_SCOPED_PTR_TYPE because wxPluralFormsNode is not
369     // fully defined yet:
370     class wxPluralFormsNodePtr
371     {
372     public:
373     wxPluralFormsNodePtr(wxPluralFormsNode *p = NULL) : m_p(p) {}
374     ~wxPluralFormsNodePtr();
375     wxPluralFormsNode& operator*() const { return *m_p; }
376     wxPluralFormsNode* operator->() const { return m_p; }
377     wxPluralFormsNode* get() const { return m_p; }
378     wxPluralFormsNode* release();
379     void reset(wxPluralFormsNode *p);
380    
381     private:
382     wxPluralFormsNode *m_p;
383     };
384    
385     class wxPluralFormsNode
386     {
387     public:
388     wxPluralFormsNode(const wxPluralFormsToken& token) : m_token(token) {}
389     const wxPluralFormsToken& token() const { return m_token; }
390     const wxPluralFormsNode* node(size_t i) const
391     { return m_nodes[i].get(); }
392     void setNode(size_t i, wxPluralFormsNode* n);
393     wxPluralFormsNode* releaseNode(size_t i);
394     wxPluralFormsToken::Number evaluate(wxPluralFormsToken::Number n) const;
395    
396     private:
397     wxPluralFormsToken m_token;
398     wxPluralFormsNodePtr m_nodes[3];
399     };
400    
401     wxPluralFormsNodePtr::~wxPluralFormsNodePtr()
402     {
403     delete m_p;
404     }
405     wxPluralFormsNode* wxPluralFormsNodePtr::release()
406     {
407     wxPluralFormsNode *p = m_p;
408     m_p = NULL;
409     return p;
410     }
411     void wxPluralFormsNodePtr::reset(wxPluralFormsNode *p)
412     {
413     if (p != m_p)
414     {
415     delete m_p;
416     m_p = p;
417     }
418     }
419    
420    
421     void wxPluralFormsNode::setNode(size_t i, wxPluralFormsNode* n)
422     {
423     m_nodes[i].reset(n);
424     }
425    
426     wxPluralFormsNode* wxPluralFormsNode::releaseNode(size_t i)
427     {
428     return m_nodes[i].release();
429     }
430    
431     wxPluralFormsToken::Number
432     wxPluralFormsNode::evaluate(wxPluralFormsToken::Number n) const
433     {
434     switch (token().type())
435     {
436     // leaf
437     case wxPluralFormsToken::T_NUMBER:
438     return token().number();
439     case wxPluralFormsToken::T_N:
440     return n;
441     // 2 args
442     case wxPluralFormsToken::T_EQUAL:
443     return node(0)->evaluate(n) == node(1)->evaluate(n);
444     case wxPluralFormsToken::T_NOT_EQUAL:
445     return node(0)->evaluate(n) != node(1)->evaluate(n);
446     case wxPluralFormsToken::T_GREATER:
447     return node(0)->evaluate(n) > node(1)->evaluate(n);
448     case wxPluralFormsToken::T_GREATER_OR_EQUAL:
449     return node(0)->evaluate(n) >= node(1)->evaluate(n);
450     case wxPluralFormsToken::T_LESS:
451     return node(0)->evaluate(n) < node(1)->evaluate(n);
452     case wxPluralFormsToken::T_LESS_OR_EQUAL:
453     return node(0)->evaluate(n) <= node(1)->evaluate(n);
454     case wxPluralFormsToken::T_REMINDER:
455     {
456     wxPluralFormsToken::Number number = node(1)->evaluate(n);
457     if (number != 0)
458     {
459     return node(0)->evaluate(n) % number;
460     }
461     else
462     {
463     return 0;
464     }
465     }
466     case wxPluralFormsToken::T_LOGICAL_AND:
467     return node(0)->evaluate(n) && node(1)->evaluate(n);
468     case wxPluralFormsToken::T_LOGICAL_OR:
469     return node(0)->evaluate(n) || node(1)->evaluate(n);
470     // 3 args
471     case wxPluralFormsToken::T_QUESTION:
472     return node(0)->evaluate(n)
473     ? node(1)->evaluate(n)
474     : node(2)->evaluate(n);
475     default:
476     return 0;
477     }
478     }
479    
480    
481     class wxPluralFormsCalculator
482     {
483     public:
484     wxPluralFormsCalculator() : m_nplurals(0), m_plural(0) {}
485    
486     // input: number, returns msgstr index
487     int evaluate(int n) const;
488    
489     // input: text after "Plural-Forms:" (e.g. "nplurals=2; plural=(n != 1);"),
490     // if s == 0, creates default handler
491     // returns 0 if error
492     static wxPluralFormsCalculator* make(const char* s = 0);
493    
494     ~wxPluralFormsCalculator() {}
495    
496     void init(wxPluralFormsToken::Number nplurals, wxPluralFormsNode* plural);
497    
498     private:
499     wxPluralFormsToken::Number m_nplurals;
500     wxPluralFormsNodePtr m_plural;
501     };
502    
503     wxDEFINE_SCOPED_PTR_TYPE(wxPluralFormsCalculator)
504    
505     void wxPluralFormsCalculator::init(wxPluralFormsToken::Number nplurals,
506     wxPluralFormsNode* plural)
507     {
508     m_nplurals = nplurals;
509     m_plural.reset(plural);
510     }
511    
512     int wxPluralFormsCalculator::evaluate(int n) const
513     {
514     if (m_plural.get() == 0)
515     {
516     return 0;
517     }
518     wxPluralFormsToken::Number number = m_plural->evaluate(n);
519     if (number < 0 || number > m_nplurals)
520     {
521     return 0;
522     }
523     return number;
524     }
525    
526    
527     class wxPluralFormsParser
528     {
529     public:
530     wxPluralFormsParser(wxPluralFormsScanner& scanner) : m_scanner(scanner) {}
531     bool parse(wxPluralFormsCalculator& rCalculator);
532    
533     private:
534     wxPluralFormsNode* parsePlural();
535     // stops at T_SEMICOLON, returns 0 if error
536     wxPluralFormsScanner& m_scanner;
537     const wxPluralFormsToken& token() const;
538     bool nextToken();
539    
540     wxPluralFormsNode* expression();
541     wxPluralFormsNode* logicalOrExpression();
542     wxPluralFormsNode* logicalAndExpression();
543     wxPluralFormsNode* equalityExpression();
544     wxPluralFormsNode* multiplicativeExpression();
545     wxPluralFormsNode* relationalExpression();
546     wxPluralFormsNode* pmExpression();
547     };
548    
549     bool wxPluralFormsParser::parse(wxPluralFormsCalculator& rCalculator)
550     {
551     if (token().type() != wxPluralFormsToken::T_NPLURALS)
552     return false;
553     if (!nextToken())
554     return false;
555     if (token().type() != wxPluralFormsToken::T_ASSIGN)
556     return false;
557     if (!nextToken())
558     return false;
559     if (token().type() != wxPluralFormsToken::T_NUMBER)
560     return false;
561     wxPluralFormsToken::Number nplurals = token().number();
562     if (!nextToken())
563     return false;
564     if (token().type() != wxPluralFormsToken::T_SEMICOLON)
565     return false;
566     if (!nextToken())
567     return false;
568     if (token().type() != wxPluralFormsToken::T_PLURAL)
569     return false;
570     if (!nextToken())
571     return false;
572     if (token().type() != wxPluralFormsToken::T_ASSIGN)
573     return false;
574     if (!nextToken())
575     return false;
576     wxPluralFormsNode* plural = parsePlural();
577     if (plural == 0)
578     return false;
579     if (token().type() != wxPluralFormsToken::T_SEMICOLON)
580     return false;
581     if (!nextToken())
582     return false;
583     if (token().type() != wxPluralFormsToken::T_EOF)
584     return false;
585     rCalculator.init(nplurals, plural);
586     return true;
587     }
588    
589     wxPluralFormsNode* wxPluralFormsParser::parsePlural()
590     {
591     wxPluralFormsNode* p = expression();
592     if (p == NULL)
593     {
594     return NULL;
595     }
596     wxPluralFormsNodePtr n(p);
597     if (token().type() != wxPluralFormsToken::T_SEMICOLON)
598     {
599     return NULL;
600     }
601     return n.release();
602     }
603    
604     const wxPluralFormsToken& wxPluralFormsParser::token() const
605     {
606     return m_scanner.token();
607     }
608    
609     bool wxPluralFormsParser::nextToken()
610     {
611     if (!m_scanner.nextToken())
612     return false;
613     return true;
614     }
615    
616     wxPluralFormsNode* wxPluralFormsParser::expression()
617     {
618     wxPluralFormsNode* p = logicalOrExpression();
619     if (p == NULL)
620     return NULL;
621     wxPluralFormsNodePtr n(p);
622     if (token().type() == wxPluralFormsToken::T_QUESTION)
623     {
624     wxPluralFormsNodePtr qn(new wxPluralFormsNode(token()));
625     if (!nextToken())
626     {
627     return 0;
628     }
629     p = expression();
630     if (p == 0)
631     {
632     return 0;
633     }
634     qn->setNode(1, p);
635     if (token().type() != wxPluralFormsToken::T_COLON)
636     {
637     return 0;
638     }
639     if (!nextToken())
640     {
641     return 0;
642     }
643     p = expression();
644     if (p == 0)
645     {
646     return 0;
647     }
648     qn->setNode(2, p);
649     qn->setNode(0, n.release());
650     return qn.release();
651     }
652     return n.release();
653     }
654    
655     wxPluralFormsNode*wxPluralFormsParser::logicalOrExpression()
656     {
657     wxPluralFormsNode* p = logicalAndExpression();
658     if (p == NULL)
659     return NULL;
660     wxPluralFormsNodePtr ln(p);
661     if (token().type() == wxPluralFormsToken::T_LOGICAL_OR)
662     {
663     wxPluralFormsNodePtr un(new wxPluralFormsNode(token()));
664     if (!nextToken())
665     {
666     return 0;
667     }
668     p = logicalOrExpression();
669     if (p == 0)
670     {
671     return 0;
672     }
673     wxPluralFormsNodePtr rn(p); // right
674     if (rn->token().type() == wxPluralFormsToken::T_LOGICAL_OR)
675     {
676     // see logicalAndExpression comment
677     un->setNode(0, ln.release());
678     un->setNode(1, rn->releaseNode(0));
679     rn->setNode(0, un.release());
680     return rn.release();
681     }
682    
683    
684     un->setNode(0, ln.release());
685     un->setNode(1, rn.release());
686     return un.release();
687     }
688     return ln.release();
689     }
690    
691     wxPluralFormsNode* wxPluralFormsParser::logicalAndExpression()
692     {
693     wxPluralFormsNode* p = equalityExpression();
694     if (p == NULL)
695     return NULL;
696     wxPluralFormsNodePtr ln(p); // left
697     if (token().type() == wxPluralFormsToken::T_LOGICAL_AND)
698     {
699     wxPluralFormsNodePtr un(new wxPluralFormsNode(token())); // up
700     if (!nextToken())
701     {
702     return NULL;
703     }
704     p = logicalAndExpression();
705     if (p == 0)
706     {
707     return NULL;
708     }
709     wxPluralFormsNodePtr rn(p); // right
710     if (rn->token().type() == wxPluralFormsToken::T_LOGICAL_AND)
711     {
712     // transform 1 && (2 && 3) -> (1 && 2) && 3
713     // u r
714     // l r -> u 3
715     // 2 3 l 2
716     un->setNode(0, ln.release());
717     un->setNode(1, rn->releaseNode(0));
718     rn->setNode(0, un.release());
719     return rn.release();
720     }
721    
722     un->setNode(0, ln.release());
723     un->setNode(1, rn.release());
724     return un.release();
725     }
726     return ln.release();
727     }
728    
729     wxPluralFormsNode* wxPluralFormsParser::equalityExpression()
730     {
731     wxPluralFormsNode* p = relationalExpression();
732     if (p == NULL)
733     return NULL;
734     wxPluralFormsNodePtr n(p);
735     if (token().type() == wxPluralFormsToken::T_EQUAL
736     || token().type() == wxPluralFormsToken::T_NOT_EQUAL)
737     {
738     wxPluralFormsNodePtr qn(new wxPluralFormsNode(token()));
739     if (!nextToken())
740     {
741     return NULL;
742     }
743     p = relationalExpression();
744     if (p == NULL)
745     {
746     return NULL;
747     }
748     qn->setNode(1, p);
749     qn->setNode(0, n.release());
750     return qn.release();
751     }
752     return n.release();
753     }
754    
755     wxPluralFormsNode* wxPluralFormsParser::relationalExpression()
756     {
757     wxPluralFormsNode* p = multiplicativeExpression();
758     if (p == NULL)
759     return NULL;
760     wxPluralFormsNodePtr n(p);
761     if (token().type() == wxPluralFormsToken::T_GREATER
762     || token().type() == wxPluralFormsToken::T_LESS
763     || token().type() == wxPluralFormsToken::T_GREATER_OR_EQUAL
764     || token().type() == wxPluralFormsToken::T_LESS_OR_EQUAL)
765     {
766     wxPluralFormsNodePtr qn(new wxPluralFormsNode(token()));
767     if (!nextToken())
768     {
769     return NULL;
770     }
771     p = multiplicativeExpression();
772     if (p == NULL)
773     {
774     return NULL;
775     }
776     qn->setNode(1, p);
777     qn->setNode(0, n.release());
778     return qn.release();
779     }
780     return n.release();
781     }
782    
783     wxPluralFormsNode* wxPluralFormsParser::multiplicativeExpression()
784     {
785     wxPluralFormsNode* p = pmExpression();
786     if (p == NULL)
787     return NULL;
788     wxPluralFormsNodePtr n(p);
789     if (token().type() == wxPluralFormsToken::T_REMINDER)
790     {
791     wxPluralFormsNodePtr qn(new wxPluralFormsNode(token()));
792     if (!nextToken())
793     {
794     return NULL;
795     }
796     p = pmExpression();
797     if (p == NULL)
798     {
799     return NULL;
800     }
801     qn->setNode(1, p);
802     qn->setNode(0, n.release());
803     return qn.release();
804     }
805     return n.release();
806     }
807    
808     wxPluralFormsNode* wxPluralFormsParser::pmExpression()
809     {
810     wxPluralFormsNodePtr n;
811     if (token().type() == wxPluralFormsToken::T_N
812     || token().type() == wxPluralFormsToken::T_NUMBER)
813     {
814     n.reset(new wxPluralFormsNode(token()));
815     if (!nextToken())
816     {
817     return NULL;
818     }
819     }
820     else if (token().type() == wxPluralFormsToken::T_LEFT_BRACKET) {
821     if (!nextToken())
822     {
823     return NULL;
824     }
825     wxPluralFormsNode* p = expression();
826     if (p == NULL)
827     {
828     return NULL;
829     }
830     n.reset(p);
831     if (token().type() != wxPluralFormsToken::T_RIGHT_BRACKET)
832     {
833     return NULL;
834     }
835     if (!nextToken())
836     {
837     return NULL;
838     }
839     }
840     else
841     {
842     return NULL;
843     }
844     return n.release();
845     }
846    
847     wxPluralFormsCalculator* wxPluralFormsCalculator::make(const char* s)
848     {
849     wxPluralFormsCalculatorPtr calculator(new wxPluralFormsCalculator);
850     if (s != NULL)
851     {
852     wxPluralFormsScanner scanner(s);
853     wxPluralFormsParser p(scanner);
854     if (!p.parse(*calculator))
855     {
856     return NULL;
857     }
858     }
859     return calculator.release();
860     }
861    
862    
863    
864    
865     // ----------------------------------------------------------------------------
866     // wxMsgCatalogFile corresponds to one disk-file message catalog.
867     //
868     // This is a "low-level" class and is used only by wxMsgCatalog
869     // ----------------------------------------------------------------------------
870    
871     WX_DECLARE_EXPORTED_STRING_HASH_MAP(wxString, wxMessagesHash);
872    
873     class wxMsgCatalogFile
874     {
875     public:
876     // ctor & dtor
877     wxMsgCatalogFile();
878     ~wxMsgCatalogFile();
879    
880     // load the catalog from disk (szDirPrefix corresponds to language)
881     bool Load(const wxChar *szDirPrefix, const wxChar *szName,
882     wxPluralFormsCalculatorPtr& rPluralFormsCalculator);
883    
884     // fills the hash with string-translation pairs
885     void FillHash(wxMessagesHash& hash,
886     const wxString& msgIdCharset,
887     bool convertEncoding) const;
888    
889     // return the charset of the strings in this catalog or empty string if
890     // none/unknown
891     wxString GetCharset() const { return m_charset; }
892    
893     private:
894     // this implementation is binary compatible with GNU gettext() version 0.10
895    
896     // an entry in the string table
897     struct wxMsgTableEntry
898     {
899     size_t32 nLen; // length of the string
900     size_t32 ofsString; // pointer to the string
901     };
902    
903     // header of a .mo file
904     struct wxMsgCatalogHeader
905     {
906     size_t32 magic, // offset +00: magic id
907     revision, // +04: revision
908     numStrings; // +08: number of strings in the file
909     size_t32 ofsOrigTable, // +0C: start of original string table
910     ofsTransTable; // +10: start of translated string table
911     size_t32 nHashSize, // +14: hash table size
912     ofsHashTable; // +18: offset of hash table start
913     };
914    
915     // all data is stored here, NULL if no data loaded
916     size_t8 *m_pData;
917    
918     // amount of memory pointed to by m_pData.
919     size_t32 m_nSize;
920    
921     // data description
922     size_t32 m_numStrings; // number of strings in this domain
923     wxMsgTableEntry *m_pOrigTable, // pointer to original strings
924     *m_pTransTable; // translated
925    
926     wxString m_charset; // from the message catalog header
927    
928    
929     // swap the 2 halves of 32 bit integer if needed
930     size_t32 Swap(size_t32 ui) const
931     {
932     return m_bSwapped ? (ui << 24) | ((ui & 0xff00) << 8) |
933     ((ui >> 8) & 0xff00) | (ui >> 24)
934     : ui;
935     }
936    
937     const char *StringAtOfs(wxMsgTableEntry *pTable, size_t32 n) const
938     {
939     const wxMsgTableEntry * const ent = pTable + n;
940    
941     // this check could fail for a corrupt message catalog
942     size_t32 ofsString = Swap(ent->ofsString);
943     if ( ofsString + Swap(ent->nLen) > m_nSize)
944     {
945     return NULL;
946     }
947    
948     return (const char *)(m_pData + ofsString);
949     }
950    
951     bool m_bSwapped; // wrong endianness?
952    
953     DECLARE_NO_COPY_CLASS(wxMsgCatalogFile)
954     };
955    
956    
957     // ----------------------------------------------------------------------------
958     // wxMsgCatalog corresponds to one loaded message catalog.
959     //
960     // This is a "low-level" class and is used only by wxLocale (that's why
961     // it's designed to be stored in a linked list)
962     // ----------------------------------------------------------------------------
963    
964     class wxMsgCatalog
965     {
966     public:
967     #if !wxUSE_UNICODE
968     wxMsgCatalog() { m_conv = NULL; }
969     ~wxMsgCatalog();
970     #endif
971    
972     // load the catalog from disk (szDirPrefix corresponds to language)
973     bool Load(const wxChar *szDirPrefix, const wxChar *szName,
974     const wxChar *msgIdCharset = NULL, bool bConvertEncoding = false);
975    
976     // get name of the catalog
977     wxString GetName() const { return m_name; }
978    
979     // get the translated string: returns NULL if not found
980     const wxChar *GetString(const wxChar *sz, size_t n = size_t(-1)) const;
981    
982     // public variable pointing to the next element in a linked list (or NULL)
983     wxMsgCatalog *m_pNext;
984    
985     private:
986     wxMessagesHash m_messages; // all messages in the catalog
987     wxString m_name; // name of the domain
988    
989     #if !wxUSE_UNICODE
990     // the conversion corresponding to this catalog charset if we installed it
991     // as the global one
992     wxCSConv *m_conv;
993     #endif
994    
995     wxPluralFormsCalculatorPtr m_pluralFormsCalculator;
996     };
997    
998     // ----------------------------------------------------------------------------
999     // global variables
1000     // ----------------------------------------------------------------------------
1001    
1002     // the list of the directories to search for message catalog files
1003     static wxArrayString gs_searchPrefixes;
1004    
1005     // ============================================================================
1006     // implementation
1007     // ============================================================================
1008    
1009     // ----------------------------------------------------------------------------
1010     // wxMsgCatalogFile class
1011     // ----------------------------------------------------------------------------
1012    
1013     wxMsgCatalogFile::wxMsgCatalogFile()
1014     {
1015     m_pData = NULL;
1016     m_nSize = 0;
1017     }
1018    
1019     wxMsgCatalogFile::~wxMsgCatalogFile()
1020     {
1021     delete [] m_pData;
1022     }
1023    
1024     // return the directories to search for message catalogs under the given
1025     // prefix, separated by wxPATH_SEP
1026     static
1027     wxString GetMsgCatalogSubdirs(const wxChar *prefix, const wxChar *lang)
1028     {
1029     // Search first in Unix-standard prefix/lang/LC_MESSAGES, then in
1030     // prefix/lang and finally in just prefix.
1031     //
1032     // Note that we use LC_MESSAGES on all platforms and not just Unix, because
1033     // it doesn't cost much to look into one more directory and doing it this
1034     // way has two important benefits:
1035     // a) we don't break compatibility with wx-2.6 and older by stopping to
1036     // look in a directory where the catalogs used to be and thus silently
1037     // breaking apps after they are recompiled against the latest wx
1038     // b) it makes it possible to package app's support files in the same
1039     // way on all target platforms
1040     wxString pathPrefix;
1041     pathPrefix << prefix << wxFILE_SEP_PATH << lang;
1042    
1043     wxString searchPath;
1044     searchPath.reserve(4*pathPrefix.length());
1045     searchPath << pathPrefix << wxFILE_SEP_PATH << wxT("LC_MESSAGES") << wxPATH_SEP
1046     << prefix << wxFILE_SEP_PATH << wxPATH_SEP
1047     << pathPrefix;
1048    
1049     return searchPath;
1050     }
1051    
1052     // construct the search path for the given language
1053     static wxString GetFullSearchPath(const wxChar *lang)
1054     {
1055     // first take the entries explicitly added by the program
1056     wxArrayString paths;
1057     paths.reserve(gs_searchPrefixes.size() + 1);
1058     size_t n,
1059     count = gs_searchPrefixes.size();
1060     for ( n = 0; n < count; n++ )
1061     {
1062     paths.Add(GetMsgCatalogSubdirs(gs_searchPrefixes[n], lang));
1063     }
1064    
1065    
1066     #if wxUSE_STDPATHS
1067     // then look in the standard location
1068     const wxString stdp = wxStandardPaths::Get().
1069     GetLocalizedResourcesDir(lang, wxStandardPaths::ResourceCat_Messages);
1070    
1071     if ( paths.Index(stdp) == wxNOT_FOUND )
1072     paths.Add(stdp);
1073     #endif // wxUSE_STDPATHS
1074    
1075     // last look in default locations
1076     #ifdef __UNIX__
1077     // LC_PATH is a standard env var containing the search path for the .mo
1078     // files
1079     const wxChar *pszLcPath = wxGetenv(wxT("LC_PATH"));
1080     if ( pszLcPath )
1081     {
1082     const wxString lcp = GetMsgCatalogSubdirs(pszLcPath, lang);
1083     if ( paths.Index(lcp) == wxNOT_FOUND )
1084     paths.Add(lcp);
1085     }
1086    
1087     // also add the one from where wxWin was installed:
1088     wxString wxp = wxGetInstallPrefix();
1089     if ( !wxp.empty() )
1090     {
1091     wxp = GetMsgCatalogSubdirs(wxp + _T("/share/locale"), lang);
1092     if ( paths.Index(wxp) == wxNOT_FOUND )
1093     paths.Add(wxp);
1094     }
1095     #endif // __UNIX__
1096    
1097    
1098     // finally construct the full search path
1099     wxString searchPath;
1100     searchPath.reserve(500);
1101     count = paths.size();
1102     for ( n = 0; n < count; n++ )
1103     {
1104     searchPath += paths[n];
1105     if ( n != count - 1 )
1106     searchPath += wxPATH_SEP;
1107     }
1108    
1109     return searchPath;
1110     }
1111    
1112     // open disk file and read in it's contents
1113     bool wxMsgCatalogFile::Load(const wxChar *szDirPrefix, const wxChar *szName,
1114     wxPluralFormsCalculatorPtr& rPluralFormsCalculator)
1115     {
1116     wxString searchPath;
1117    
1118     #if wxUSE_FONTMAP
1119     // first look for the catalog for this language and the current locale:
1120     // notice that we don't use the system name for the locale as this would
1121     // force us to install catalogs in different locations depending on the
1122     // system but always use the canonical name
1123     wxFontEncoding encSys = wxLocale::GetSystemEncoding();
1124     if ( encSys != wxFONTENCODING_SYSTEM )
1125     {
1126     wxString fullname(szDirPrefix);
1127     fullname << _T('.') << wxFontMapperBase::GetEncodingName(encSys);
1128     searchPath << GetFullSearchPath(fullname) << wxPATH_SEP;
1129     }
1130     #endif // wxUSE_FONTMAP
1131    
1132    
1133     searchPath += GetFullSearchPath(szDirPrefix);
1134     const wxChar *sublocale = wxStrchr(szDirPrefix, wxT('_'));
1135     if ( sublocale )
1136     {
1137     // also add just base locale name: for things like "fr_BE" (belgium
1138     // french) we should use "fr" if no belgium specific message catalogs
1139     // exist
1140     searchPath << wxPATH_SEP
1141     << GetFullSearchPath(wxString(szDirPrefix).
1142     Left((size_t)(sublocale - szDirPrefix)));
1143     }
1144    
1145     // don't give translation errors here because the wxstd catalog might
1146     // not yet be loaded (and it's normal)
1147     //
1148     // (we're using an object because we have several return paths)
1149    
1150     NoTransErr noTransErr;
1151     wxLogVerbose(_("looking for catalog '%s' in path '%s'."),
1152     szName, searchPath.c_str());
1153     wxLogTrace(TRACE_I18N, _T("Looking for \"%s.mo\" in \"%s\""),
1154     szName, searchPath.c_str());
1155    
1156     wxFileName fn(szName);
1157     fn.SetExt(_T("mo"));
1158     wxString strFullName;
1159     if ( !wxFindFileInPath(&strFullName, searchPath, fn.GetFullPath()) ) {
1160     wxLogVerbose(_("catalog file for domain '%s' not found."), szName);
1161     wxLogTrace(TRACE_I18N, _T("Catalog \"%s.mo\" not found"), szName);
1162     return false;
1163     }
1164    
1165     // open file
1166     wxLogVerbose(_("using catalog '%s' from '%s'."), szName, strFullName.c_str());
1167     wxLogTrace(TRACE_I18N, _T("Using catalog \"%s\"."), strFullName.c_str());
1168    
1169     wxFile fileMsg(strFullName);
1170     if ( !fileMsg.IsOpened() )
1171     return false;
1172    
1173     // get the file size (assume it is less than 4Gb...)
1174     wxFileOffset lenFile = fileMsg.Length();
1175     if ( lenFile == wxInvalidOffset )
1176     return false;
1177    
1178     size_t nSize = wx_truncate_cast(size_t, lenFile);
1179     wxASSERT_MSG( nSize == lenFile + size_t(0), _T("message catalog bigger than 4GB?") );
1180    
1181     // read the whole file in memory
1182     m_pData = new size_t8[nSize];
1183     if ( fileMsg.Read(m_pData, nSize) != lenFile ) {
1184     wxDELETEA(m_pData);
1185     return false;
1186     }
1187    
1188     // examine header
1189     bool bValid = nSize + (size_t)0 > sizeof(wxMsgCatalogHeader);
1190    
1191     wxMsgCatalogHeader *pHeader = (wxMsgCatalogHeader *)m_pData;
1192     if ( bValid ) {
1193     // we'll have to swap all the integers if it's true
1194     m_bSwapped = pHeader->magic == MSGCATALOG_MAGIC_SW;
1195    
1196     // check the magic number
1197     bValid = m_bSwapped || pHeader->magic == MSGCATALOG_MAGIC;
1198     }
1199    
1200     if ( !bValid ) {
1201     // it's either too short or has incorrect magic number
1202     wxLogWarning(_("'%s' is not a valid message catalog."), strFullName.c_str());
1203    
1204     wxDELETEA(m_pData);
1205     return false;
1206     }
1207    
1208     // initialize
1209     m_numStrings = Swap(pHeader->numStrings);
1210     m_pOrigTable = (wxMsgTableEntry *)(m_pData +
1211     Swap(pHeader->ofsOrigTable));
1212     m_pTransTable = (wxMsgTableEntry *)(m_pData +
1213     Swap(pHeader->ofsTransTable));
1214     m_nSize = (size_t32)nSize;
1215    
1216     // now parse catalog's header and try to extract catalog charset and
1217     // plural forms formula from it:
1218    
1219     const char* headerData = StringAtOfs(m_pOrigTable, 0);
1220     if (headerData && headerData[0] == 0)
1221     {
1222     // Extract the charset:
1223     wxString header = wxString::FromAscii(StringAtOfs(m_pTransTable, 0));
1224     int begin = header.Find(wxT("Content-Type: text/plain; charset="));
1225     if (begin != wxNOT_FOUND)
1226     {
1227     begin += 34; //strlen("Content-Type: text/plain; charset=")
1228     size_t end = header.find('\n', begin);
1229     if (end != size_t(-1))
1230     {
1231     m_charset.assign(header, begin, end - begin);
1232     if (m_charset == wxT("CHARSET"))
1233     {
1234     // "CHARSET" is not valid charset, but lazy translator
1235     m_charset.Clear();
1236     }
1237     }
1238     }
1239     // else: incorrectly filled Content-Type header
1240    
1241     // Extract plural forms:
1242     begin = header.Find(wxT("Plural-Forms:"));
1243     if (begin != wxNOT_FOUND)
1244     {
1245     begin += 13;
1246     size_t end = header.find('\n', begin);
1247     if (end != size_t(-1))
1248     {
1249     wxString pfs(header, begin, end - begin);
1250     wxPluralFormsCalculator* pCalculator = wxPluralFormsCalculator
1251     ::make(pfs.ToAscii());
1252     if (pCalculator != 0)
1253     {
1254     rPluralFormsCalculator.reset(pCalculator);
1255     }
1256     else
1257     {
1258     wxLogVerbose(_("Cannot parse Plural-Forms:'%s'"), pfs.c_str());
1259     }
1260     }
1261     }
1262     if (rPluralFormsCalculator.get() == NULL)
1263     {
1264     rPluralFormsCalculator.reset(wxPluralFormsCalculator::make());
1265     }
1266     }
1267    
1268     // everything is fine
1269     return true;
1270     }
1271    
1272     void wxMsgCatalogFile::FillHash(wxMessagesHash& hash,
1273     const wxString& msgIdCharset,
1274     bool convertEncoding) const
1275     {
1276     #if wxUSE_UNICODE
1277     // this parameter doesn't make sense, we always must convert encoding in
1278     // Unicode build
1279     convertEncoding = true;
1280     #elif wxUSE_FONTMAP
1281     if ( convertEncoding )
1282     {
1283     // determine if we need any conversion at all
1284     wxFontEncoding encCat = wxFontMapperBase::GetEncodingFromName(m_charset);
1285     if ( encCat == wxLocale::GetSystemEncoding() )
1286     {
1287     // no need to convert
1288     convertEncoding = false;
1289     }
1290     }
1291     #endif // wxUSE_UNICODE/wxUSE_FONTMAP
1292    
1293     #if wxUSE_WCHAR_T
1294     // conversion to use to convert catalog strings to the GUI encoding
1295     wxMBConv *inputConv,
1296     *inputConvPtr = NULL; // same as inputConv but safely deleteable
1297     if ( convertEncoding && !m_charset.empty() )
1298     {
1299     inputConvPtr =
1300     inputConv = new wxCSConv(m_charset);
1301     }
1302     else // no need or not possible to convert the encoding
1303     {
1304     #if wxUSE_UNICODE
1305     // we must somehow convert the narrow strings in the message catalog to
1306     // wide strings, so use the default conversion if we have no charset
1307     inputConv = wxConvCurrent;
1308     #else // !wxUSE_UNICODE
1309     inputConv = NULL;
1310     #endif // wxUSE_UNICODE/!wxUSE_UNICODE
1311     }
1312    
1313     // conversion to apply to msgid strings before looking them up: we only
1314     // need it if the msgids are neither in 7 bit ASCII nor in the same
1315     // encoding as the catalog
1316     wxCSConv *sourceConv = msgIdCharset.empty() || (msgIdCharset == m_charset)
1317     ? NULL
1318     : new wxCSConv(msgIdCharset);
1319    
1320     #elif wxUSE_FONTMAP
1321     wxASSERT_MSG( msgIdCharset.empty(),
1322     _T("non-ASCII msgid languages only supported if wxUSE_WCHAR_T=1") );
1323    
1324     wxEncodingConverter converter;
1325     if ( convertEncoding )
1326     {
1327     wxFontEncoding targetEnc = wxFONTENCODING_SYSTEM;
1328     wxFontEncoding enc = wxFontMapperBase::Get()->CharsetToEncoding(m_charset, false);
1329     if ( enc == wxFONTENCODING_SYSTEM )
1330     {
1331     convertEncoding = false; // unknown encoding
1332     }
1333     else
1334     {
1335     targetEnc = wxLocale::GetSystemEncoding();
1336     if (targetEnc == wxFONTENCODING_SYSTEM)
1337     {
1338     wxFontEncodingArray a = wxEncodingConverter::GetPlatformEquivalents(enc);
1339     if (a[0] == enc)
1340     // no conversion needed, locale uses native encoding
1341     convertEncoding = false;
1342     if (a.GetCount() == 0)
1343     // we don't know common equiv. under this platform
1344     convertEncoding = false;
1345     targetEnc = a[0];
1346     }
1347     }
1348    
1349     if ( convertEncoding )
1350     {
1351     converter.Init(enc, targetEnc);
1352     }
1353     }
1354     #endif // wxUSE_WCHAR_T/!wxUSE_WCHAR_T
1355     (void)convertEncoding; // get rid of warnings about unused parameter
1356    
1357     for (size_t32 i = 0; i < m_numStrings; i++)
1358     {
1359     const char *data = StringAtOfs(m_pOrigTable, i);
1360    
1361     wxString msgid;
1362     #if wxUSE_UNICODE
1363     msgid = wxString(data, *inputConv);
1364     #else // ASCII
1365     #if wxUSE_WCHAR_T
1366     if ( inputConv && sourceConv )
1367     msgid = wxString(inputConv->cMB2WC(data), *sourceConv);
1368     else
1369     #endif
1370     msgid = data;
1371     #endif // wxUSE_UNICODE
1372    
1373     data = StringAtOfs(m_pTransTable, i);
1374     size_t length = Swap(m_pTransTable[i].nLen);
1375     size_t offset = 0;
1376     size_t index = 0;
1377     while (offset < length)
1378     {
1379     const char * const str = data + offset;
1380    
1381     wxString msgstr;
1382     #if wxUSE_UNICODE
1383     msgstr = wxString(str, *inputConv);
1384     #elif wxUSE_WCHAR_T
1385     if ( inputConv )
1386     msgstr = wxString(inputConv->cMB2WC(str), *wxConvUI);
1387     else
1388     msgstr = str;
1389     #else // !wxUSE_WCHAR_T
1390     #if wxUSE_FONTMAP
1391     if ( convertEncoding )
1392     msgstr = wxString(converter.Convert(str));
1393     else
1394     #endif
1395     msgstr = str;
1396     #endif // wxUSE_WCHAR_T/!wxUSE_WCHAR_T
1397    
1398     if ( !msgstr.empty() )
1399     {
1400     hash[index == 0 ? msgid : msgid + wxChar(index)] = msgstr;
1401     }
1402    
1403     // skip this string
1404     offset += strlen(str) + 1;
1405     ++index;
1406     }
1407     }
1408    
1409     #if wxUSE_WCHAR_T
1410     delete sourceConv;
1411     delete inputConvPtr;
1412     #endif // wxUSE_WCHAR_T
1413     }
1414    
1415    
1416     // ----------------------------------------------------------------------------
1417     // wxMsgCatalog class
1418     // ----------------------------------------------------------------------------
1419    
1420     #if !wxUSE_UNICODE
1421     wxMsgCatalog::~wxMsgCatalog()
1422     {
1423     if ( m_conv )
1424     {
1425     if ( wxConvUI == m_conv )
1426     {
1427     // we only change wxConvUI if it points to wxConvLocal so we reset
1428     // it back to it too
1429     wxConvUI = &wxConvLocal;
1430     }
1431    
1432     delete m_conv;
1433     }
1434     }
1435     #endif // !wxUSE_UNICODE
1436    
1437     bool wxMsgCatalog::Load(const wxChar *szDirPrefix, const wxChar *szName,
1438     const wxChar *msgIdCharset, bool bConvertEncoding)
1439     {
1440     wxMsgCatalogFile file;
1441    
1442     m_name = szName;
1443    
1444     if ( !file.Load(szDirPrefix, szName, m_pluralFormsCalculator) )
1445     return false;
1446    
1447     file.FillHash(m_messages, msgIdCharset, bConvertEncoding);
1448    
1449     #if !wxUSE_UNICODE && wxUSE_WCHAR_T
1450     // we should use a conversion compatible with the message catalog encoding
1451     // in the GUI if we don't convert the strings to the current conversion but
1452     // as the encoding is global, only change it once, otherwise we could get
1453     // into trouble if we use several message catalogs with different encodings
1454     //
1455     // this is, of course, a hack but it at least allows the program to use
1456     // message catalogs in any encodings without asking the user to change his
1457     // locale
1458     if ( !bConvertEncoding &&
1459     !file.GetCharset().empty() &&
1460     wxConvUI == &wxConvLocal )
1461     {
1462     wxConvUI =
1463     m_conv = new wxCSConv(file.GetCharset());
1464     }
1465     #endif // !wxUSE_UNICODE && wxUSE_WCHAR_T
1466    
1467     return true;
1468     }
1469    
1470     const wxChar *wxMsgCatalog::GetString(const wxChar *sz, size_t n) const
1471     {
1472     int index = 0;
1473     if (n != size_t(-1))
1474     {
1475     index = m_pluralFormsCalculator->evaluate(n);
1476     }
1477     wxMessagesHash::const_iterator i;
1478     if (index != 0)
1479     {
1480     i = m_messages.find(wxString(sz) + wxChar(index)); // plural
1481     }
1482     else
1483     {
1484     i = m_messages.find(sz);
1485     }
1486    
1487     if ( i != m_messages.end() )
1488     {
1489     return i->second.c_str();
1490     }
1491     else
1492     return NULL;
1493     }
1494    
1495     // ----------------------------------------------------------------------------
1496     // wxLocale
1497     // ----------------------------------------------------------------------------
1498    
1499     #include "wx/arrimpl.cpp"
1500     WX_DECLARE_EXPORTED_OBJARRAY(wxLanguageInfo, wxLanguageInfoArray);
1501     WX_DEFINE_OBJARRAY(wxLanguageInfoArray)
1502    
1503     wxLanguageInfoArray *wxLocale::ms_languagesDB = NULL;
1504    
1505     /*static*/ void wxLocale::CreateLanguagesDB()
1506     {
1507     if (ms_languagesDB == NULL)
1508     {
1509     ms_languagesDB = new wxLanguageInfoArray;
1510     InitLanguagesDB();
1511     }
1512     }
1513    
1514     /*static*/ void wxLocale::DestroyLanguagesDB()
1515     {
1516     delete ms_languagesDB;
1517     ms_languagesDB = NULL;
1518     }
1519    
1520    
1521     void wxLocale::DoCommonInit()
1522     {
1523     m_pszOldLocale = NULL;
1524    
1525     m_pOldLocale = wxSetLocale(this);
1526    
1527     m_pMsgCat = NULL;
1528     m_language = wxLANGUAGE_UNKNOWN;
1529     m_initialized = false;
1530     }
1531    
1532     // NB: this function has (desired) side effect of changing current locale
1533     bool wxLocale::Init(const wxChar *szName,
1534     const wxChar *szShort,
1535     const wxChar *szLocale,
1536     bool bLoadDefault,
1537     bool bConvertEncoding)
1538     {
1539     wxASSERT_MSG( !m_initialized,
1540     _T("you can't call wxLocale::Init more than once") );
1541    
1542     m_initialized = true;
1543     m_strLocale = szName;
1544     m_strShort = szShort;
1545     m_bConvertEncoding = bConvertEncoding;
1546     m_language = wxLANGUAGE_UNKNOWN;
1547    
1548     // change current locale (default: same as long name)
1549     if ( szLocale == NULL )
1550     {
1551     // the argument to setlocale()
1552     szLocale = szShort;
1553    
1554     wxCHECK_MSG( szLocale, false, _T("no locale to set in wxLocale::Init()") );
1555     }
1556    
1557     #ifdef __WXWINCE__
1558     // FIXME: I'm guessing here
1559     wxChar localeName[256];
1560     int ret = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SLANGUAGE, localeName,
1561     256);
1562     if (ret != 0)
1563     {
1564     m_pszOldLocale = wxStrdup(localeName);
1565     }
1566     else
1567     m_pszOldLocale = NULL;
1568    
1569     // TODO: how to find languageId
1570     // SetLocaleInfo(languageId, SORT_DEFAULT, localeName);
1571     #else
1572     wxMB2WXbuf oldLocale = wxSetlocale(LC_ALL, szLocale);
1573     if ( oldLocale )
1574     m_pszOldLocale = wxStrdup(oldLocale);
1575     else
1576     m_pszOldLocale = NULL;
1577     #endif
1578    
1579     if ( m_pszOldLocale == NULL )
1580     wxLogError(_("locale '%s' can not be set."), szLocale);
1581    
1582     // the short name will be used to look for catalog files as well,
1583     // so we need something here
1584     if ( m_strShort.empty() ) {
1585     // FIXME I don't know how these 2 letter abbreviations are formed,
1586     // this wild guess is surely wrong
1587     if ( szLocale && szLocale[0] )
1588     {
1589     m_strShort += (wxChar)wxTolower(szLocale[0]);
1590     if ( szLocale[1] )
1591     m_strShort += (wxChar)wxTolower(szLocale[1]);
1592     }
1593     }
1594    
1595     // load the default catalog with wxWidgets standard messages
1596     m_pMsgCat = NULL;
1597     bool bOk = true;
1598     if ( bLoadDefault )
1599     {
1600     bOk = AddCatalog(wxT("wxstd"));
1601    
1602     // there may be a catalog with toolkit specific overrides, it is not
1603     // an error if this does not exist
1604     if ( bOk )
1605     {
1606     wxString port(wxPlatformInfo::Get().GetPortIdName());
1607     if ( !port.empty() )
1608     {
1609     AddCatalog(port.BeforeFirst(wxT('/')).MakeLower());
1610     }
1611     }
1612     }
1613    
1614     return bOk;
1615     }
1616    
1617    
1618     #if defined(__UNIX__) && wxUSE_UNICODE && !defined(__WXMAC__)
1619     static wxWCharBuffer wxSetlocaleTryUTF(int c, const wxChar *lc)
1620     {
1621     wxMB2WXbuf l = wxSetlocale(c, lc);
1622     if ( !l && lc && lc[0] != 0 )
1623     {
1624     wxString buf(lc);
1625     wxString buf2;
1626     buf2 = buf + wxT(".UTF-8");
1627     l = wxSetlocale(c, buf2.c_str());
1628     if ( !l )
1629     {
1630     buf2 = buf + wxT(".utf-8");
1631     l = wxSetlocale(c, buf2.c_str());
1632     }
1633     if ( !l )
1634     {
1635     buf2 = buf + wxT(".UTF8");
1636     l = wxSetlocale(c, buf2.c_str());
1637     }
1638     if ( !l )
1639     {
1640     buf2 = buf + wxT(".utf8");
1641     l = wxSetlocale(c, buf2.c_str());
1642     }
1643     }
1644     return l;
1645     }
1646     #else
1647     #define wxSetlocaleTryUTF(c, lc) wxSetlocale(c, lc)
1648     #endif
1649    
1650     bool wxLocale::Init(int language, int flags)
1651     {
1652     int lang = language;
1653     if (lang == wxLANGUAGE_DEFAULT)
1654     {
1655     // auto detect the language
1656     lang = GetSystemLanguage();
1657     }
1658    
1659     // We failed to detect system language, so we will use English:
1660     if (lang == wxLANGUAGE_UNKNOWN)
1661     {
1662     return false;
1663     }
1664    
1665     const wxLanguageInfo *info = GetLanguageInfo(lang);
1666    
1667     // Unknown language:
1668     if (info == NULL)
1669     {
1670     wxLogError(wxT("Unknown language %i."), lang);
1671     return false;
1672     }
1673    
1674     wxString name = info->Description;
1675     wxString canonical = info->CanonicalName;
1676     wxString locale;
1677    
1678     // Set the locale:
1679     #if defined(__OS2__)
1680     wxMB2WXbuf retloc = wxSetlocale(LC_ALL , wxEmptyString);
1681     #elif defined(__UNIX__) && !defined(__WXMAC__)
1682     if (language != wxLANGUAGE_DEFAULT)
1683     locale = info->CanonicalName;
1684    
1685     wxMB2WXbuf retloc = wxSetlocaleTryUTF(LC_ALL, locale);
1686    
1687     const wxString langOnly = locale.Left(2);
1688     if ( !retloc )
1689     {
1690     // Some C libraries don't like xx_YY form and require xx only
1691     retloc = wxSetlocaleTryUTF(LC_ALL, langOnly);
1692     }
1693    
1694     #if wxUSE_FONTMAP
1695     // some systems (e.g. FreeBSD and HP-UX) don't have xx_YY aliases but
1696     // require the full xx_YY.encoding form, so try using UTF-8 because this is
1697     // the only thing we can do generically
1698     //
1699     // TODO: add encodings applicable to each language to the lang DB and try
1700     // them all in turn here
1701     if ( !retloc )
1702     {
1703     const wxChar **names =
1704     wxFontMapperBase::GetAllEncodingNames(wxFONTENCODING_UTF8);
1705     while ( *names )
1706     {
1707     retloc = wxSetlocale(LC_ALL, locale + _T('.') + *names++);
1708     if ( retloc )
1709     break;
1710     }
1711     }
1712     #endif // wxUSE_FONTMAP
1713    
1714     if ( !retloc )
1715     {
1716     // Some C libraries (namely glibc) still use old ISO 639,
1717     // so will translate the abbrev for them
1718     wxString localeAlt;
1719     if ( langOnly == wxT("he") )
1720     localeAlt = wxT("iw") + locale.Mid(3);
1721     else if ( langOnly == wxT("id") )
1722     localeAlt = wxT("in") + locale.Mid(3);
1723     else if ( langOnly == wxT("yi") )
1724     localeAlt = wxT("ji") + locale.Mid(3);
1725     else if ( langOnly == wxT("nb") )
1726     localeAlt = wxT("no_NO");
1727     else if ( langOnly == wxT("nn") )
1728     localeAlt = wxT("no_NY");
1729    
1730     if ( !localeAlt.empty() )
1731     {
1732     retloc = wxSetlocaleTryUTF(LC_ALL, localeAlt);
1733     if ( !retloc )
1734     retloc = wxSetlocaleTryUTF(LC_ALL, localeAlt.Left(2));
1735     }
1736     }
1737    
1738     if ( !retloc )
1739     {
1740     wxLogError(wxT("Cannot set locale to '%s'."), locale.c_str());
1741     return false;
1742     }
1743    
1744     #ifdef __AIX__
1745     // at least in AIX 5.2 libc is buggy and the string returned from setlocale(LC_ALL)
1746     // can't be passed back to it because it returns 6 strings (one for each locale
1747     // category), i.e. for C locale we get back "C C C C C C"
1748     //
1749     // this contradicts IBM own docs but this is not of much help, so just work around
1750     // it in the crudest possible manner
1751     wxChar *p = wxStrchr((wxChar *)retloc, _T(' '));
1752     if ( p )
1753     *p = _T('\0');
1754     #endif // __AIX__
1755    
1756     #elif defined(__WIN32__)
1757    
1758     #if wxUSE_UNICODE && (defined(__VISUALC__) || defined(__MINGW32__))
1759     // NB: setlocale() from msvcrt.dll (used by VC++ and Mingw)
1760     // can't set locale to language that can only be written using
1761     // Unicode. Therefore wxSetlocale call failed, but we don't want
1762     // to report it as an error -- so that at least message catalogs
1763     // can be used. Watch for code marked with
1764     // #ifdef SETLOCALE_FAILS_ON_UNICODE_LANGS bellow.
1765     #define SETLOCALE_FAILS_ON_UNICODE_LANGS
1766     #endif
1767    
1768     #if !wxUSE_UNICODE
1769     const
1770     #endif
1771     wxMB2WXbuf retloc = wxT("C");
1772     if (language != wxLANGUAGE_DEFAULT)
1773     {
1774     if (info->WinLang == 0)
1775     {
1776     wxLogWarning(wxT("Locale '%s' not supported by OS."), name.c_str());
1777     // retloc already set to "C"
1778     }
1779     else
1780     {
1781     int codepage
1782     #ifdef SETLOCALE_FAILS_ON_UNICODE_LANGS
1783     = -1
1784     #endif
1785     ;
1786     wxUint32 lcid = MAKELCID(MAKELANGID(info->WinLang, info->WinSublang),
1787     SORT_DEFAULT);
1788     // FIXME
1789     #ifndef __WXWINCE__
1790     SetThreadLocale(lcid);
1791     #endif
1792     // NB: we must translate LCID to CRT's setlocale string ourselves,
1793     // because SetThreadLocale does not modify change the
1794     // interpretation of setlocale(LC_ALL, "") call:
1795     wxChar buffer[256];
1796     buffer[0] = wxT('\0');
1797     GetLocaleInfo(lcid, LOCALE_SENGLANGUAGE, buffer, 256);
1798     locale << buffer;
1799     if (GetLocaleInfo(lcid, LOCALE_SENGCOUNTRY, buffer, 256) > 0)
1800     locale << wxT("_") << buffer;
1801     if (GetLocaleInfo(lcid, LOCALE_IDEFAULTANSICODEPAGE, buffer, 256) > 0)
1802     {
1803     codepage = wxAtoi(buffer);
1804     if (codepage != 0)
1805     locale << wxT(".") << buffer;
1806     }
1807     if (locale.empty())
1808     {
1809     wxLogLastError(wxT("SetThreadLocale"));
1810     wxLogError(wxT("Cannot set locale to language %s."), name.c_str());
1811     return false;
1812     }
1813     else
1814     {
1815     // FIXME
1816     #ifndef __WXWINCE__
1817     retloc = wxSetlocale(LC_ALL, locale);
1818     #endif
1819     #ifdef SETLOCALE_FAILS_ON_UNICODE_LANGS
1820     if (codepage == 0 && (const wxChar*)retloc == NULL)
1821     {
1822     retloc = wxT("C");
1823     }
1824     #endif
1825     }
1826     }
1827     }
1828     else
1829     {
1830     // FIXME
1831     #ifndef __WXWINCE__
1832     retloc = wxSetlocale(LC_ALL, wxEmptyString);
1833     #else
1834     retloc = NULL;
1835     #endif
1836     #ifdef SETLOCALE_FAILS_ON_UNICODE_LANGS
1837     if ((const wxChar*)retloc == NULL)
1838     {
1839     wxChar buffer[16];
1840     if (GetLocaleInfo(LOCALE_USER_DEFAULT,
1841     LOCALE_IDEFAULTANSICODEPAGE, buffer, 16) > 0 &&
1842     wxStrcmp(buffer, wxT("0")) == 0)
1843     {
1844     retloc = wxT("C");
1845     }
1846     }
1847     #endif
1848     }
1849    
1850     if ( !retloc )
1851     {
1852     wxLogError(wxT("Cannot set locale to language %s."), name.c_str());
1853     return false;
1854     }
1855     #elif defined(__WXMAC__)
1856     if (lang == wxLANGUAGE_DEFAULT)
1857     locale = wxEmptyString;
1858     else
1859     locale = info->CanonicalName;
1860    
1861     wxMB2WXbuf retloc = wxSetlocale(LC_ALL, locale);
1862    
1863     if ( !retloc )
1864     {
1865     // Some C libraries don't like xx_YY form and require xx only
1866     retloc = wxSetlocale(LC_ALL, locale.Mid(0,2));
1867     }
1868     if ( !retloc )
1869     {
1870     wxLogError(wxT("Cannot set locale to '%s'."), locale.c_str());
1871     return false;
1872     }
1873     #else
1874     wxUnusedVar(flags);
1875     return false;
1876     #define WX_NO_LOCALE_SUPPORT
1877     #endif
1878    
1879     #ifndef WX_NO_LOCALE_SUPPORT
1880     wxChar *szLocale = retloc ? wxStrdup(retloc) : NULL;
1881     bool ret = Init(name, canonical, szLocale,
1882     (flags & wxLOCALE_LOAD_DEFAULT) != 0,
1883     (flags & wxLOCALE_CONV_ENCODING) != 0);
1884     free(szLocale);
1885    
1886     if (IsOk()) // setlocale() succeeded
1887     m_language = lang;
1888    
1889     return ret;
1890     #endif // !WX_NO_LOCALE_SUPPORT
1891     }
1892    
1893    
1894    
1895     void wxLocale::AddCatalogLookupPathPrefix(const wxString& prefix)
1896     {
1897     if ( gs_searchPrefixes.Index(prefix) == wxNOT_FOUND )
1898     {
1899     gs_searchPrefixes.Add(prefix);
1900     }
1901     //else: already have it
1902     }
1903    
1904     /*static*/ int wxLocale::GetSystemLanguage()
1905     {
1906     CreateLanguagesDB();
1907    
1908     // init i to avoid compiler warning
1909     size_t i = 0,
1910     count = ms_languagesDB->GetCount();
1911    
1912     #if defined(__UNIX__) && !defined(__WXMAC__)
1913     // first get the string identifying the language from the environment
1914     wxString langFull;
1915     if (!wxGetEnv(wxT("LC_ALL"), &langFull) &&
1916     !wxGetEnv(wxT("LC_MESSAGES"), &langFull) &&
1917     !wxGetEnv(wxT("LANG"), &langFull))
1918     {
1919     // no language specified, treat it as English
1920     return wxLANGUAGE_ENGLISH_US;
1921     }
1922    
1923     if ( langFull == _T("C") || langFull == _T("POSIX") )
1924     {
1925     // default C locale is English too
1926     return wxLANGUAGE_ENGLISH_US;
1927     }
1928    
1929     // the language string has the following form
1930     //
1931     // lang[_LANG][.encoding][@modifier]
1932     //
1933     // (see environ(5) in the Open Unix specification)
1934     //
1935     // where lang is the primary language, LANG is a sublang/territory,
1936     // encoding is the charset to use and modifier "allows the user to select
1937     // a specific instance of localization data within a single category"
1938     //
1939     // for example, the following strings are valid:
1940     // fr
1941     // fr_FR
1942     // de_DE.iso88591
1943     // de_DE@euro
1944     // de_DE.iso88591@euro
1945    
1946     // for now we don't use the encoding, although we probably should (doing
1947     // translations of the msg catalogs on the fly as required) (TODO)
1948     //
1949     // we need the modified for languages like Valencian: ca_ES@valencia
1950     // though, remember it
1951     wxString modifier;
1952     size_t posModifier = langFull.find_first_of(_T("@"));
1953     if ( posModifier != wxString::npos )
1954     modifier = langFull.Mid(posModifier);
1955    
1956     size_t posEndLang = langFull.find_first_of(_T("@."));
1957     if ( posEndLang != wxString::npos )
1958     {
1959     langFull.Truncate(posEndLang);
1960     }
1961    
1962     // in addition to the format above, we also can have full language names
1963     // in LANG env var - for example, SuSE is known to use LANG="german" - so
1964     // check for this
1965    
1966     // do we have just the language (or sublang too)?
1967     bool justLang = langFull.length() == LEN_LANG;
1968     if ( justLang ||
1969     (langFull.length() == LEN_FULL && langFull[LEN_LANG] == wxT('_')) )
1970     {
1971     // 0. Make sure the lang is according to latest ISO 639
1972     // (this is necessary because glibc uses iw and in instead
1973     // of he and id respectively).
1974    
1975     // the language itself (second part is the dialect/sublang)
1976     wxString langOrig = ExtractLang(langFull);
1977    
1978     wxString lang;
1979     if ( langOrig == wxT("iw"))
1980     lang = _T("he");
1981     else if (langOrig == wxT("in"))
1982     lang = wxT("id");
1983     else if (langOrig == wxT("ji"))
1984     lang = wxT("yi");
1985     else if (langOrig == wxT("no_NO"))
1986     lang = wxT("nb_NO");
1987     else if (langOrig == wxT("no_NY"))
1988     lang = wxT("nn_NO");
1989     else if (langOrig == wxT("no"))
1990     lang = wxT("nb_NO");
1991     else
1992     lang = langOrig;
1993    
1994     // did we change it?
1995     if ( lang != langOrig )
1996     {
1997     langFull = lang + ExtractNotLang(langFull);
1998     }
1999    
2000     // 1. Try to find the language either as is:
2001     // a) With modifier if set
2002     if ( !modifier.empty() )
2003     {
2004     wxString langFullWithModifier = langFull + modifier;
2005     for ( i = 0; i < count; i++ )
2006     {
2007     if ( ms_languagesDB->Item(i).CanonicalName == langFullWithModifier )
2008     break;
2009     }
2010     }
2011    
2012     // b) Without modifier
2013     if ( modifier.empty() || i == count )
2014     {
2015     for ( i = 0; i < count; i++ )
2016     {
2017     if ( ms_languagesDB->Item(i).CanonicalName == langFull )
2018     break;
2019     }
2020     }
2021    
2022     // 2. If langFull is of the form xx_YY, try to find xx:
2023     if ( i == count && !justLang )
2024     {
2025     for ( i = 0; i < count; i++ )
2026     {
2027     if ( ms_languagesDB->Item(i).CanonicalName == lang )
2028     {
2029     break;
2030     }
2031     }
2032     }
2033    
2034     // 3. If langFull is of the form xx, try to find any xx_YY record:
2035     if ( i == count && justLang )
2036     {
2037     for ( i = 0; i < count; i++ )
2038     {
2039     if ( ExtractLang(ms_languagesDB->Item(i).CanonicalName)
2040     == langFull )
2041     {
2042     break;
2043     }
2044     }
2045     }
2046     }
2047     else // not standard format
2048     {
2049     // try to find the name in verbose description
2050     for ( i = 0; i < count; i++ )
2051     {
2052     if (ms_languagesDB->Item(i).Description.CmpNoCase(langFull) == 0)
2053     {
2054     break;
2055     }
2056     }
2057     }
2058     #elif defined(__WXMAC__)
2059     const wxChar * lc = NULL ;
2060     long lang = GetScriptVariable( smSystemScript, smScriptLang) ;
2061     switch( GetScriptManagerVariable( smRegionCode ) ) {
2062     case verUS :
2063     lc = wxT("en_US") ;
2064     break ;
2065     case verFrance :
2066     lc = wxT("fr_FR") ;
2067     break ;
2068     case verBritain :
2069     lc = wxT("en_GB") ;
2070     break ;
2071     case verGermany :
2072     lc = wxT("de_DE") ;
2073     break ;
2074     case verItaly :
2075     lc = wxT("it_IT") ;
2076     break ;
2077     case verNetherlands :
2078     lc = wxT("nl_NL") ;
2079     break ;
2080     case verFlemish :
2081     lc = wxT("nl_BE") ;
2082     break ;
2083     case verSweden :
2084     lc = wxT("sv_SE" );
2085     break ;
2086     case verSpain :
2087     lc = wxT("es_ES" );
2088     break ;
2089     case verDenmark :
2090     lc = wxT("da_DK") ;
2091     break ;
2092     case verPortugal :
2093     lc = wxT("pt_PT") ;
2094     break ;
2095     case verFrCanada:
2096     lc = wxT("fr_CA") ;
2097     break ;
2098     case verNorway:
2099     lc = wxT("nb_NO") ;
2100     break ;
2101     case verIsrael:
2102     lc = wxT("iw_IL") ;
2103     break ;
2104     case verJapan:
2105     lc = wxT("ja_JP") ;
2106     break ;
2107     case verAustralia:
2108     lc = wxT("en_AU") ;
2109     break ;
2110     case verArabic:
2111     lc = wxT("ar") ;
2112     break ;
2113     case verFinland:
2114     lc = wxT("fi_FI") ;
2115     break ;
2116     case verFrSwiss:
2117     lc = wxT("fr_CH") ;
2118     break ;
2119     case verGrSwiss:
2120     lc = wxT("de_CH") ;
2121     break ;
2122     case verGreece:
2123     lc = wxT("el_GR") ;
2124     break ;
2125     case verIceland:
2126     lc = wxT("is_IS") ;
2127     break ;
2128     case verMalta:
2129     lc = wxT("mt_MT") ;
2130     break ;
2131     case verCyprus:
2132     // _CY is not part of wx, so we have to translate according to the system language
2133     if ( lang == langGreek ) {
2134     lc = wxT("el_GR") ;
2135     }
2136     else if ( lang == langTurkish ) {
2137     lc = wxT("tr_TR") ;
2138     }
2139     break ;
2140     case verTurkey:
2141     lc = wxT("tr_TR") ;
2142     break ;
2143     case verYugoCroatian:
2144     lc = wxT("hr_HR") ;
2145     break ;
2146     case verIndiaHindi:
2147     lc = wxT("hi_IN") ;
2148     break ;
2149     case verPakistanUrdu:
2150     lc = wxT("ur_PK") ;
2151     break ;
2152     case verTurkishModified:
2153     lc = wxT("tr_TR") ;
2154     break ;
2155     case verItalianSwiss:
2156     lc = wxT("it_CH") ;
2157     break ;
2158     case verInternational:
2159     lc = wxT("en") ;
2160     break ;
2161     case verRomania:
2162     lc = wxT("ro_RO") ;
2163     break ;
2164     case verGreecePoly:
2165     lc = wxT("el_GR") ;
2166     break ;
2167     case verLithuania:
2168     lc = wxT("lt_LT") ;
2169     break ;
2170     case verPoland:
2171     lc = wxT("pl_PL") ;
2172     break ;
2173     case verMagyar :
2174     case verHungary:
2175     lc = wxT("hu_HU") ;
2176     break ;
2177     case verEstonia:
2178     lc = wxT("et_EE") ;
2179     break ;
2180     case verLatvia:
2181     lc = wxT("lv_LV") ;
2182     break ;
2183     case verSami:
2184     lc = wxT("se_NO") ;
2185     break ;
2186     case verFaroeIsl:
2187     lc = wxT("fo_FO") ;
2188     break ;
2189     case verIran:
2190     lc = wxT("fa_IR") ;
2191     break ;
2192     case verRussia:
2193     lc = wxT("ru_RU") ;
2194     break ;
2195     case verIreland:
2196     lc = wxT("ga_IE") ;
2197     break ;
2198     case verKorea:
2199     lc = wxT("ko_KR") ;
2200     break ;
2201     case verChina:
2202     lc = wxT("zh_CN") ;
2203     break ;
2204     case verTaiwan:
2205     lc = wxT("zh_TW") ;
2206     break ;
2207     case verThailand:
2208     lc = wxT("th_TH") ;
2209     break ;
2210     case verCzech:
2211     lc = wxT("cs_CZ") ;
2212     break ;
2213     case verSlovak:
2214     lc = wxT("sk_SK") ;
2215     break ;
2216     case verBengali:
2217     lc = wxT("bn") ;
2218     break ;
2219     case verByeloRussian:
2220     lc = wxT("be_BY") ;
2221     break ;
2222     case verUkraine:
2223     lc = wxT("uk_UA") ;
2224     break ;
2225     case verGreeceAlt:
2226     lc = wxT("el_GR") ;
2227     break ;
2228     case verSerbian:
2229     lc = wxT("sr_YU") ;
2230     break ;
2231     case verSlovenian:
2232     lc = wxT("sl_SI") ;
2233     break ;
2234     case verMacedonian:
2235     lc = wxT("mk_MK") ;
2236     break ;
2237     case verCroatia:
2238     lc = wxT("hr_HR") ;
2239     break ;
2240     case verBrazil:
2241     lc = wxT("pt_BR ") ;
2242     break ;
2243     case verBulgaria:
2244     lc = wxT("bg_BG") ;
2245     break ;
2246     case verCatalonia:
2247     lc = wxT("ca_ES") ;
2248     break ;
2249     case verScottishGaelic:
2250     lc = wxT("gd") ;
2251     break ;
2252     case verManxGaelic:
2253     lc = wxT("gv") ;
2254     break ;
2255     case verBreton:
2256     lc = wxT("br") ;
2257     break ;
2258     case verNunavut:
2259     lc = wxT("iu_CA") ;
2260     break ;
2261     case verWelsh:
2262     lc = wxT("cy") ;
2263     break ;
2264     case verIrishGaelicScript:
2265     lc = wxT("ga_IE") ;
2266     break ;
2267     case verEngCanada:
2268     lc = wxT("en_CA") ;
2269     break ;
2270     case verBhutan:
2271     lc = wxT("dz_BT") ;
2272     break ;
2273     case verArmenian:
2274     lc = wxT("hy_AM") ;
2275     break ;
2276     case verGeorgian:
2277     lc = wxT("ka_GE") ;
2278     break ;
2279     case verSpLatinAmerica:
2280     lc = wxT("es_AR") ;
2281     break ;
2282     case verTonga:
2283     lc = wxT("to_TO" );
2284     break ;
2285     case verFrenchUniversal:
2286     lc = wxT("fr_FR") ;
2287     break ;
2288     case verAustria:
2289     lc = wxT("de_AT") ;
2290     break ;
2291     case verGujarati:
2292     lc = wxT("gu_IN") ;
2293     break ;
2294     case verPunjabi:
2295     lc = wxT("pa") ;
2296     break ;
2297     case verIndiaUrdu:
2298     lc = wxT("ur_IN") ;
2299     break ;
2300     case verVietnam:
2301     lc = wxT("vi_VN") ;
2302     break ;
2303     case verFrBelgium:
2304     lc = wxT("fr_BE") ;
2305     break ;
2306     case verUzbek:
2307     lc = wxT("uz_UZ") ;
2308     break ;
2309     case verSingapore:
2310     lc = wxT("zh_SG") ;
2311     break ;
2312     case verNynorsk:
2313     lc = wxT("nn_NO") ;
2314     break ;
2315     case verAfrikaans:
2316     lc = wxT("af_ZA") ;
2317     break ;
2318     case verEsperanto:
2319     lc = wxT("eo") ;
2320     break ;
2321     case verMarathi:
2322     lc = wxT("mr_IN") ;
2323     break ;
2324     case verTibetan:
2325     lc = wxT("bo") ;
2326     break ;
2327     case verNepal:
2328     lc = wxT("ne_NP") ;
2329     break ;
2330     case verGreenland:
2331     lc = wxT("kl_GL") ;
2332     break ;
2333     default :
2334     break ;
2335     }
2336     if ( !lc )
2337     return wxLANGUAGE_UNKNOWN;
2338     for ( i = 0; i < count; i++ )
2339     {
2340     if ( ms_languagesDB->Item(i).CanonicalName == lc )
2341     {
2342     break;
2343     }
2344     }
2345    
2346     #elif defined(__WIN32__)
2347     LCID lcid = GetUserDefaultLCID();
2348     if ( lcid != 0 )
2349     {
2350     wxUint32 lang = PRIMARYLANGID(LANGIDFROMLCID(lcid));
2351     wxUint32 sublang = SUBLANGID(LANGIDFROMLCID(lcid));
2352    
2353     for ( i = 0; i < count; i++ )
2354     {
2355     if (ms_languagesDB->Item(i).WinLang == lang &&
2356     ms_languagesDB->Item(i).WinSublang == sublang)
2357     {
2358     break;
2359     }
2360     }
2361     }
2362     //else: leave wxlang == wxLANGUAGE_UNKNOWN
2363     #endif // Unix/Win32
2364    
2365     if ( i < count )
2366     {
2367     // we did find a matching entry, use it
2368     return ms_languagesDB->Item(i).Language;
2369     }
2370    
2371     // no info about this language in the database
2372     return wxLANGUAGE_UNKNOWN;
2373     }
2374    
2375     // ----------------------------------------------------------------------------
2376     // encoding stuff
2377     // ----------------------------------------------------------------------------
2378    
2379     // this is a bit strange as under Windows we get the encoding name using its
2380     // numeric value and under Unix we do it the other way round, but this just
2381     // reflects the way different systems provide the encoding info
2382    
2383     /* static */
2384     wxString wxLocale::GetSystemEncodingName()
2385     {
2386     wxString encname;
2387    
2388     #if defined(__WIN32__) && !defined(__WXMICROWIN__)
2389     // FIXME: what is the error return value for GetACP()?
2390     UINT codepage = ::GetACP();
2391     encname.Printf(_T("windows-%u"), codepage);
2392     #elif defined(__WXMAC__)
2393     // default is just empty string, this resolves to the default system
2394     // encoding later
2395     #elif defined(__UNIX_LIKE__)
2396    
2397     #if defined(HAVE_LANGINFO_H) && defined(CODESET)
2398     // GNU libc provides current character set this way (this conforms
2399     // to Unix98)
2400     char *oldLocale = strdup(setlocale(LC_CTYPE, NULL));
2401     setlocale(LC_CTYPE, "");
2402     const char *alang = nl_langinfo(CODESET);
2403     setlocale(LC_CTYPE, oldLocale);
2404     free(oldLocale);
2405    
2406     if ( alang )
2407     {
2408     encname = wxString::FromAscii( alang );
2409     }
2410     else // nl_langinfo() failed
2411     #endif // HAVE_LANGINFO_H
2412     {
2413     // if we can't get at the character set directly, try to see if it's in
2414     // the environment variables (in most cases this won't work, but I was
2415     // out of ideas)
2416     char *lang = getenv( "LC_ALL");
2417     char *dot = lang ? strchr(lang, '.') : (char *)NULL;
2418     if (!dot)
2419     {
2420     lang = getenv( "LC_CTYPE" );
2421     if ( lang )
2422     dot = strchr(lang, '.' );
2423     }
2424     if (!dot)
2425     {
2426     lang = getenv( "LANG");
2427     if ( lang )
2428     dot = strchr(lang, '.');
2429     }
2430    
2431     if ( dot )
2432     {
2433     encname = wxString::FromAscii( dot+1 );
2434     }
2435     }
2436     #endif // Win32/Unix
2437    
2438     return encname;
2439     }
2440    
2441     /* static */
2442     wxFontEncoding wxLocale::GetSystemEncoding()
2443     {
2444     #if defined(__WIN32__) && !defined(__WXMICROWIN__)
2445     UINT codepage = ::GetACP();
2446    
2447     // wxWidgets only knows about CP1250-1257, 874, 932, 936, 949, 950
2448     if ( codepage >= 1250 && codepage <= 1257 )
2449     {
2450     return (wxFontEncoding)(wxFONTENCODING_CP1250 + codepage - 1250);
2451     }
2452    
2453     if ( codepage == 874 )
2454     {
2455     return wxFONTENCODING_CP874;
2456     }
2457    
2458     if ( codepage == 932 )
2459     {
2460     return wxFONTENCODING_CP932;
2461     }
2462    
2463     if ( codepage == 936 )
2464     {
2465     return wxFONTENCODING_CP936;
2466     }
2467    
2468     if ( codepage == 949 )
2469     {
2470     return wxFONTENCODING_CP949;
2471     }
2472    
2473     if ( codepage == 950 )
2474     {
2475     return wxFONTENCODING_CP950;
2476     }
2477     #elif defined(__WXMAC__)
2478     TextEncoding encoding = 0 ;
2479     #if TARGET_CARBON
2480     encoding = CFStringGetSystemEncoding() ;
2481     #else
2482     UpgradeScriptInfoToTextEncoding ( smSystemScript , kTextLanguageDontCare , kTextRegionDontCare , NULL , &encoding ) ;
2483     #endif
2484     return wxMacGetFontEncFromSystemEnc( encoding ) ;
2485     #elif defined(__UNIX_LIKE__) && wxUSE_FONTMAP
2486     const wxString encname = GetSystemEncodingName();
2487     if ( !encname.empty() )
2488     {
2489     wxFontEncoding enc = wxFontMapperBase::GetEncodingFromName(encname);
2490    
2491     // on some modern Linux systems (RedHat 8) the default system locale
2492     // is UTF8 -- but it isn't supported by wxGTK1 in ANSI build at all so
2493     // don't even try to use it in this case
2494     #if !wxUSE_UNICODE && \
2495     ((defined(__WXGTK__) && !defined(__WXGTK20__)) || defined(__WXMOTIF__))
2496     if ( enc == wxFONTENCODING_UTF8 )
2497     {
2498     // the most similar supported encoding...
2499     enc = wxFONTENCODING_ISO8859_1;
2500     }
2501     #endif // !wxUSE_UNICODE
2502    
2503     // GetEncodingFromName() returns wxFONTENCODING_DEFAULT for C locale
2504     // (a.k.a. US-ASCII) which is arguably a bug but keep it like this for
2505     // backwards compatibility and just take care to not return
2506     // wxFONTENCODING_DEFAULT from here as this surely doesn't make sense
2507     if ( enc == wxFONTENCODING_DEFAULT )
2508     {
2509     // we don't have wxFONTENCODING_ASCII, so use the closest one
2510     return wxFONTENCODING_ISO8859_1;
2511     }
2512    
2513     if ( enc != wxFONTENCODING_MAX )
2514     {
2515     return enc;
2516     }
2517     //else: return wxFONTENCODING_SYSTEM below
2518     }
2519     #endif // Win32/Unix
2520    
2521     return wxFONTENCODING_SYSTEM;
2522     }
2523    
2524     /* static */
2525     void wxLocale::AddLanguage(const wxLanguageInfo& info)
2526     {
2527     CreateLanguagesDB();
2528     ms_languagesDB->Add(info);
2529     }
2530    
2531     /* static */
2532     const wxLanguageInfo *wxLocale::GetLanguageInfo(int lang)
2533     {
2534     CreateLanguagesDB();
2535    
2536     // calling GetLanguageInfo(wxLANGUAGE_DEFAULT) is a natural thing to do, so
2537     // make it work
2538     if ( lang == wxLANGUAGE_DEFAULT )
2539     lang = GetSystemLanguage();
2540    
2541     const size_t count = ms_languagesDB->GetCount();
2542     for ( size_t i = 0; i < count; i++ )
2543     {
2544     if ( ms_languagesDB->Item(i).Language == lang )
2545     {
2546     // We need to create a temporary here in order to make this work with BCC in final build mode
2547     wxLanguageInfo *ptr = &ms_languagesDB->Item(i);
2548     return ptr;
2549     }
2550     }
2551    
2552     return NULL;
2553     }
2554    
2555     /* static */
2556     wxString wxLocale::GetLanguageName(int lang)
2557     {
2558     const wxLanguageInfo *info = GetLanguageInfo(lang);
2559     if ( !info )
2560     return wxEmptyString;
2561     else
2562     return info->Description;
2563     }
2564    
2565     /* static */
2566     const wxLanguageInfo *wxLocale::FindLanguageInfo(const wxString& locale)
2567     {
2568     CreateLanguagesDB();
2569    
2570     const wxLanguageInfo *infoRet = NULL;
2571    
2572     const size_t count = ms_languagesDB->GetCount();
2573     for ( size_t i = 0; i < count; i++ )
2574     {
2575     const wxLanguageInfo *info = &ms_languagesDB->Item(i);
2576    
2577     if ( wxStricmp(locale, info->CanonicalName) == 0 ||
2578     wxStricmp(locale, info->Description) == 0 )
2579     {
2580     // exact match, stop searching
2581     infoRet = info;
2582     break;
2583     }
2584    
2585     if ( wxStricmp(locale, info->CanonicalName.BeforeFirst(_T('_'))) == 0 )
2586     {
2587     // a match -- but maybe we'll find an exact one later, so continue
2588     // looking
2589     //
2590     // OTOH, maybe we had already found a language match and in this
2591     // case don't overwrite it becauce the entry for the default
2592     // country always appears first in ms_languagesDB
2593     if ( !infoRet )
2594     infoRet = info;
2595     }
2596     }
2597    
2598     return infoRet;
2599     }
2600    
2601     wxString wxLocale::GetSysName() const
2602     {
2603     // FIXME
2604     #ifndef __WXWINCE__
2605     return wxSetlocale(LC_ALL, NULL);
2606     #else
2607     return wxEmptyString;
2608     #endif
2609     }
2610    
2611     // clean up
2612     wxLocale::~wxLocale()
2613     {
2614     // free memory
2615     wxMsgCatalog *pTmpCat;
2616     while ( m_pMsgCat != NULL ) {
2617     pTmpCat = m_pMsgCat;
2618     m_pMsgCat = m_pMsgCat->m_pNext;
2619     delete pTmpCat;
2620     }
2621    
2622     // restore old locale pointer
2623     wxSetLocale(m_pOldLocale);
2624    
2625     // FIXME
2626     #ifndef __WXWINCE__
2627     wxSetlocale(LC_ALL, m_pszOldLocale);
2628     #endif
2629     free((wxChar *)m_pszOldLocale); // const_cast
2630     }
2631    
2632     // get the translation of given string in current locale
2633     const wxChar *wxLocale::GetString(const wxChar *szOrigString,
2634     const wxChar *szDomain) const
2635     {
2636     return GetString(szOrigString, szOrigString, size_t(-1), szDomain);
2637     }
2638    
2639     const wxChar *wxLocale::GetString(const wxChar *szOrigString,
2640     const wxChar *szOrigString2,
2641     size_t n,
2642     const wxChar *szDomain) const
2643     {
2644     if ( wxIsEmpty(szOrigString) )
2645     return wxEmptyString;
2646    
2647     const wxChar *pszTrans = NULL;
2648     wxMsgCatalog *pMsgCat;
2649    
2650     if ( szDomain != NULL && szDomain[0] )
2651     {
2652     pMsgCat = FindCatalog(szDomain);
2653    
2654     // does the catalog exist?
2655     if ( pMsgCat != NULL )
2656     pszTrans = pMsgCat->GetString(szOrigString, n);
2657     }
2658     else
2659     {
2660     // search in all domains
2661     for ( pMsgCat = m_pMsgCat; pMsgCat != NULL; pMsgCat = pMsgCat->m_pNext )
2662     {
2663     pszTrans = pMsgCat->GetString(szOrigString, n);
2664     if ( pszTrans != NULL ) // take the first found
2665     break;
2666     }
2667     }
2668    
2669     if ( pszTrans == NULL )
2670     {
2671     #ifdef __WXDEBUG__
2672     if ( !NoTransErr::Suppress() )
2673     {
2674     NoTransErr noTransErr;
2675    
2676     wxLogTrace(TRACE_I18N,
2677     _T("string \"%s\"[%ld] not found in %slocale '%s'."),
2678     szOrigString, (long)n,
2679     szDomain ? wxString::Format(_T("domain '%s' "), szDomain).c_str()
2680     : _T(""),
2681     m_strLocale.c_str());
2682     }
2683     #endif // __WXDEBUG__
2684    
2685     if (n == size_t(-1))
2686     return szOrigString;
2687     else
2688     return n == 1 ? szOrigString : szOrigString2;
2689     }
2690    
2691     return pszTrans;
2692     }
2693    
2694     wxString wxLocale::GetHeaderValue( const wxChar* szHeader,
2695     const wxChar* szDomain ) const
2696     {
2697     if ( wxIsEmpty(szHeader) )
2698     return wxEmptyString;
2699    
2700     wxChar const * pszTrans = NULL;
2701     wxMsgCatalog *pMsgCat;
2702    
2703     if ( szDomain != NULL )
2704     {
2705     pMsgCat = FindCatalog(szDomain);
2706    
2707     // does the catalog exist?
2708     if ( pMsgCat == NULL )
2709     return wxEmptyString;
2710    
2711     pszTrans = pMsgCat->GetString(wxEmptyString, (size_t)-1);
2712     }
2713     else
2714     {
2715     // search in all domains
2716     for ( pMsgCat = m_pMsgCat; pMsgCat != NULL; pMsgCat = pMsgCat->m_pNext )
2717     {
2718     pszTrans = pMsgCat->GetString(wxEmptyString, (size_t)-1);
2719     if ( pszTrans != NULL ) // take the first found
2720     break;
2721     }
2722     }
2723    
2724     if ( wxIsEmpty(pszTrans) )
2725     return wxEmptyString;
2726    
2727     wxChar const * pszFound = wxStrstr(pszTrans, szHeader);
2728     if ( pszFound == NULL )
2729     return wxEmptyString;
2730    
2731     pszFound += wxStrlen(szHeader) + 2 /* ': ' */;
2732    
2733     // Every header is separated by \n
2734    
2735     wxChar const * pszEndLine = wxStrchr(pszFound, wxT('\n'));
2736     if ( pszEndLine == NULL ) pszEndLine = pszFound + wxStrlen(pszFound);
2737    
2738    
2739     // wxString( wxChar*, length);
2740     wxString retVal( pszFound, pszEndLine - pszFound );
2741    
2742     return retVal;
2743     }
2744    
2745    
2746     // find catalog by name in a linked list, return NULL if !found
2747     wxMsgCatalog *wxLocale::FindCatalog(const wxChar *szDomain) const
2748     {
2749     // linear search in the linked list
2750     wxMsgCatalog *pMsgCat;
2751     for ( pMsgCat = m_pMsgCat; pMsgCat != NULL; pMsgCat = pMsgCat->m_pNext )
2752     {
2753     if ( wxStricmp(pMsgCat->GetName(), szDomain) == 0 )
2754     return pMsgCat;
2755     }
2756    
2757     return NULL;
2758     }
2759    
2760     // check if the given locale is provided by OS and C run time
2761     /* static */
2762     bool wxLocale::IsAvailable(int lang)
2763     {
2764     const wxLanguageInfo *info = wxLocale::GetLanguageInfo(lang);
2765     wxCHECK_MSG( info, false, _T("invalid language") );
2766    
2767     #if defined(__WIN32__)
2768     if ( !info->WinLang )
2769     return false;
2770    
2771     if ( !::IsValidLocale
2772     (
2773     MAKELCID(MAKELANGID(info->WinLang, info->WinSublang),
2774     SORT_DEFAULT),
2775