Parent Directory
|
Revision Log
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 |