~brian-sidebotham/wxwidgets-cmake/wxpython-2.9.4

« back to all changes in this revision

Viewing changes to src/common/translation.cpp

  • Committer: Brian Sidebotham
  • Date: 2013-08-03 14:30:08 UTC
  • Revision ID: brian.sidebotham@gmail.com-20130803143008-c7806tkych1tp6fc
Initial import into Bazaar

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/////////////////////////////////////////////////////////////////////////////
 
2
// Name:        src/common/translation.cpp
 
3
// Purpose:     Internationalization and localisation for wxWidgets
 
4
// Author:      Vadim Zeitlin, Vaclav Slavik,
 
5
//              Michael N. Filippov <michael@idisys.iae.nsk.su>
 
6
//              (2003/09/30 - PluralForms support)
 
7
// Created:     2010-04-23
 
8
// RCS-ID:      $Id: translation.cpp 71375 2012-05-07 13:12:27Z 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
#if wxUSE_INTL
 
29
 
 
30
#ifndef WX_PRECOMP
 
31
    #include "wx/dynarray.h"
 
32
    #include "wx/string.h"
 
33
    #include "wx/intl.h"
 
34
    #include "wx/log.h"
 
35
    #include "wx/utils.h"
 
36
    #include "wx/hashmap.h"
 
37
    #include "wx/module.h"
 
38
#endif // WX_PRECOMP
 
39
 
 
40
// standard headers
 
41
#include <ctype.h>
 
42
#include <stdlib.h>
 
43
 
 
44
#include "wx/arrstr.h"
 
45
#include "wx/dir.h"
 
46
#include "wx/file.h"
 
47
#include "wx/filename.h"
 
48
#include "wx/tokenzr.h"
 
49
#include "wx/fontmap.h"
 
50
#include "wx/stdpaths.h"
 
51
#include "wx/hashset.h"
 
52
 
 
53
#ifdef __WINDOWS__
 
54
    #include "wx/msw/wrapwin.h"
 
55
#endif
 
56
 
 
57
// ----------------------------------------------------------------------------
 
58
// simple types
 
59
// ----------------------------------------------------------------------------
 
60
 
 
61
typedef wxUint32 size_t32;
 
62
 
 
63
// ----------------------------------------------------------------------------
 
64
// constants
 
65
// ----------------------------------------------------------------------------
 
66
 
 
67
// magic number identifying the .mo format file
 
68
const size_t32 MSGCATALOG_MAGIC    = 0x950412de;
 
69
const size_t32 MSGCATALOG_MAGIC_SW = 0xde120495;
 
70
 
 
71
#define TRACE_I18N wxS("i18n")
 
72
 
 
73
// ============================================================================
 
74
// implementation
 
75
// ============================================================================
 
76
 
 
77
namespace
 
78
{
 
79
 
 
80
#if !wxUSE_UNICODE
 
81
// We need to keep track of (char*) msgids in non-Unicode legacy builds. Instead
 
82
// of making the public wxMsgCatalog and wxTranslationsLoader APIs ugly, we
 
83
// store them in this global map.
 
84
wxStringToStringHashMap gs_msgIdCharset;
 
85
#endif
 
86
 
 
87
} // anonymous namespace
 
88
 
 
89
// ----------------------------------------------------------------------------
 
90
// Plural forms parser
 
91
// ----------------------------------------------------------------------------
 
92
 
 
93
/*
 
94
                                Simplified Grammar
 
95
 
 
96
Expression:
 
97
    LogicalOrExpression '?' Expression ':' Expression
 
98
    LogicalOrExpression
 
99
 
 
100
LogicalOrExpression:
 
101
    LogicalAndExpression "||" LogicalOrExpression   // to (a || b) || c
 
102
    LogicalAndExpression
 
103
 
 
104
LogicalAndExpression:
 
105
    EqualityExpression "&&" LogicalAndExpression    // to (a && b) && c
 
106
    EqualityExpression
 
107
 
 
108
EqualityExpression:
 
109
    RelationalExpression "==" RelationalExperession
 
110
    RelationalExpression "!=" RelationalExperession
 
111
    RelationalExpression
 
112
 
 
113
RelationalExpression:
 
114
    MultiplicativeExpression '>' MultiplicativeExpression
 
115
    MultiplicativeExpression '<' MultiplicativeExpression
 
116
    MultiplicativeExpression ">=" MultiplicativeExpression
 
117
    MultiplicativeExpression "<=" MultiplicativeExpression
 
118
    MultiplicativeExpression
 
119
 
 
120
MultiplicativeExpression:
 
121
    PmExpression '%' PmExpression
 
122
    PmExpression
 
123
 
 
124
PmExpression:
 
125
    N
 
126
    Number
 
127
    '(' Expression ')'
 
128
*/
 
129
 
 
130
class wxPluralFormsToken
 
131
{
 
132
public:
 
133
    enum Type
 
134
    {
 
135
        T_ERROR, T_EOF, T_NUMBER, T_N, T_PLURAL, T_NPLURALS, T_EQUAL, T_ASSIGN,
 
136
        T_GREATER, T_GREATER_OR_EQUAL, T_LESS, T_LESS_OR_EQUAL,
 
137
        T_REMINDER, T_NOT_EQUAL,
 
138
        T_LOGICAL_AND, T_LOGICAL_OR, T_QUESTION, T_COLON, T_SEMICOLON,
 
139
        T_LEFT_BRACKET, T_RIGHT_BRACKET
 
140
    };
 
141
    Type type() const { return m_type; }
 
142
    void setType(Type type) { m_type = type; }
 
143
    // for T_NUMBER only
 
144
    typedef int Number;
 
145
    Number number() const { return m_number; }
 
146
    void setNumber(Number num) { m_number = num; }
 
147
private:
 
148
    Type m_type;
 
149
    Number m_number;
 
150
};
 
151
 
 
152
 
 
153
class wxPluralFormsScanner
 
154
{
 
155
public:
 
156
    wxPluralFormsScanner(const char* s);
 
157
    const wxPluralFormsToken& token() const { return m_token; }
 
158
    bool nextToken();  // returns false if error
 
159
private:
 
160
    const char* m_s;
 
161
    wxPluralFormsToken m_token;
 
162
};
 
163
 
 
164
wxPluralFormsScanner::wxPluralFormsScanner(const char* s) : m_s(s)
 
165
{
 
166
    nextToken();
 
167
}
 
168
 
 
169
bool wxPluralFormsScanner::nextToken()
 
170
{
 
171
    wxPluralFormsToken::Type type = wxPluralFormsToken::T_ERROR;
 
172
    while (isspace((unsigned char) *m_s))
 
173
    {
 
174
        ++m_s;
 
175
    }
 
176
    if (*m_s == 0)
 
177
    {
 
178
        type = wxPluralFormsToken::T_EOF;
 
179
    }
 
180
    else if (isdigit((unsigned char) *m_s))
 
181
    {
 
182
        wxPluralFormsToken::Number number = *m_s++ - '0';
 
183
        while (isdigit((unsigned char) *m_s))
 
184
        {
 
185
            number = number * 10 + (*m_s++ - '0');
 
186
        }
 
187
        m_token.setNumber(number);
 
188
        type = wxPluralFormsToken::T_NUMBER;
 
189
    }
 
190
    else if (isalpha((unsigned char) *m_s))
 
191
    {
 
192
        const char* begin = m_s++;
 
193
        while (isalnum((unsigned char) *m_s))
 
194
        {
 
195
            ++m_s;
 
196
        }
 
197
        size_t size = m_s - begin;
 
198
        if (size == 1 && memcmp(begin, "n", size) == 0)
 
199
        {
 
200
            type = wxPluralFormsToken::T_N;
 
201
        }
 
202
        else if (size == 6 && memcmp(begin, "plural", size) == 0)
 
203
        {
 
204
            type = wxPluralFormsToken::T_PLURAL;
 
205
        }
 
206
        else if (size == 8 && memcmp(begin, "nplurals", size) == 0)
 
207
        {
 
208
            type = wxPluralFormsToken::T_NPLURALS;
 
209
        }
 
210
    }
 
211
    else if (*m_s == '=')
 
212
    {
 
213
        ++m_s;
 
214
        if (*m_s == '=')
 
215
        {
 
216
            ++m_s;
 
217
            type = wxPluralFormsToken::T_EQUAL;
 
218
        }
 
219
        else
 
220
        {
 
221
            type = wxPluralFormsToken::T_ASSIGN;
 
222
        }
 
223
    }
 
224
    else if (*m_s == '>')
 
225
    {
 
226
        ++m_s;
 
227
        if (*m_s == '=')
 
228
        {
 
229
            ++m_s;
 
230
            type = wxPluralFormsToken::T_GREATER_OR_EQUAL;
 
231
        }
 
232
        else
 
233
        {
 
234
            type = wxPluralFormsToken::T_GREATER;
 
235
        }
 
236
    }
 
237
    else if (*m_s == '<')
 
238
    {
 
239
        ++m_s;
 
240
        if (*m_s == '=')
 
241
        {
 
242
            ++m_s;
 
243
            type = wxPluralFormsToken::T_LESS_OR_EQUAL;
 
244
        }
 
245
        else
 
246
        {
 
247
            type = wxPluralFormsToken::T_LESS;
 
248
        }
 
249
    }
 
250
    else if (*m_s == '%')
 
251
    {
 
252
        ++m_s;
 
253
        type = wxPluralFormsToken::T_REMINDER;
 
254
    }
 
255
    else if (*m_s == '!' && m_s[1] == '=')
 
256
    {
 
257
        m_s += 2;
 
258
        type = wxPluralFormsToken::T_NOT_EQUAL;
 
259
    }
 
260
    else if (*m_s == '&' && m_s[1] == '&')
 
261
    {
 
262
        m_s += 2;
 
263
        type = wxPluralFormsToken::T_LOGICAL_AND;
 
264
    }
 
265
    else if (*m_s == '|' && m_s[1] == '|')
 
266
    {
 
267
        m_s += 2;
 
268
        type = wxPluralFormsToken::T_LOGICAL_OR;
 
269
    }
 
270
    else if (*m_s == '?')
 
271
    {
 
272
        ++m_s;
 
273
        type = wxPluralFormsToken::T_QUESTION;
 
274
    }
 
275
    else if (*m_s == ':')
 
276
    {
 
277
        ++m_s;
 
278
        type = wxPluralFormsToken::T_COLON;
 
279
    } else if (*m_s == ';') {
 
280
        ++m_s;
 
281
        type = wxPluralFormsToken::T_SEMICOLON;
 
282
    }
 
283
    else if (*m_s == '(')
 
284
    {
 
285
        ++m_s;
 
286
        type = wxPluralFormsToken::T_LEFT_BRACKET;
 
287
    }
 
288
    else if (*m_s == ')')
 
289
    {
 
290
        ++m_s;
 
291
        type = wxPluralFormsToken::T_RIGHT_BRACKET;
 
292
    }
 
293
    m_token.setType(type);
 
294
    return type != wxPluralFormsToken::T_ERROR;
 
295
}
 
296
 
 
297
class wxPluralFormsNode;
 
298
 
 
299
// NB: Can't use wxDEFINE_SCOPED_PTR_TYPE because wxPluralFormsNode is not
 
300
//     fully defined yet:
 
301
class wxPluralFormsNodePtr
 
302
{
 
303
public:
 
304
    wxPluralFormsNodePtr(wxPluralFormsNode *p = NULL) : m_p(p) {}
 
305
    ~wxPluralFormsNodePtr();
 
306
    wxPluralFormsNode& operator*() const { return *m_p; }
 
307
    wxPluralFormsNode* operator->() const { return m_p; }
 
308
    wxPluralFormsNode* get() const { return m_p; }
 
309
    wxPluralFormsNode* release();
 
310
    void reset(wxPluralFormsNode *p);
 
311
 
 
312
private:
 
313
    wxPluralFormsNode *m_p;
 
314
};
 
315
 
 
316
class wxPluralFormsNode
 
317
{
 
318
public:
 
319
    wxPluralFormsNode(const wxPluralFormsToken& token) : m_token(token) {}
 
320
    const wxPluralFormsToken& token() const { return m_token; }
 
321
    const wxPluralFormsNode* node(unsigned i) const
 
322
        { return m_nodes[i].get(); }
 
323
    void setNode(unsigned i, wxPluralFormsNode* n);
 
324
    wxPluralFormsNode* releaseNode(unsigned i);
 
325
    wxPluralFormsToken::Number evaluate(wxPluralFormsToken::Number n) const;
 
326
 
 
327
private:
 
328
    wxPluralFormsToken m_token;
 
329
    wxPluralFormsNodePtr m_nodes[3];
 
330
};
 
331
 
 
332
wxPluralFormsNodePtr::~wxPluralFormsNodePtr()
 
333
{
 
334
    delete m_p;
 
335
}
 
336
wxPluralFormsNode* wxPluralFormsNodePtr::release()
 
337
{
 
338
    wxPluralFormsNode *p = m_p;
 
339
    m_p = NULL;
 
340
    return p;
 
341
}
 
342
void wxPluralFormsNodePtr::reset(wxPluralFormsNode *p)
 
343
{
 
344
    if (p != m_p)
 
345
    {
 
346
        delete m_p;
 
347
        m_p = p;
 
348
    }
 
349
}
 
350
 
 
351
 
 
352
void wxPluralFormsNode::setNode(unsigned i, wxPluralFormsNode* n)
 
353
{
 
354
    m_nodes[i].reset(n);
 
355
}
 
356
 
 
357
wxPluralFormsNode*  wxPluralFormsNode::releaseNode(unsigned i)
 
358
{
 
359
    return m_nodes[i].release();
 
360
}
 
361
 
 
362
wxPluralFormsToken::Number
 
363
wxPluralFormsNode::evaluate(wxPluralFormsToken::Number n) const
 
364
{
 
365
    switch (token().type())
 
366
    {
 
367
        // leaf
 
368
        case wxPluralFormsToken::T_NUMBER:
 
369
            return token().number();
 
370
        case wxPluralFormsToken::T_N:
 
371
            return n;
 
372
        // 2 args
 
373
        case wxPluralFormsToken::T_EQUAL:
 
374
            return node(0)->evaluate(n) == node(1)->evaluate(n);
 
375
        case wxPluralFormsToken::T_NOT_EQUAL:
 
376
            return node(0)->evaluate(n) != node(1)->evaluate(n);
 
377
        case wxPluralFormsToken::T_GREATER:
 
378
            return node(0)->evaluate(n) > node(1)->evaluate(n);
 
379
        case wxPluralFormsToken::T_GREATER_OR_EQUAL:
 
380
            return node(0)->evaluate(n) >= node(1)->evaluate(n);
 
381
        case wxPluralFormsToken::T_LESS:
 
382
            return node(0)->evaluate(n) < node(1)->evaluate(n);
 
383
        case wxPluralFormsToken::T_LESS_OR_EQUAL:
 
384
            return node(0)->evaluate(n) <= node(1)->evaluate(n);
 
385
        case wxPluralFormsToken::T_REMINDER:
 
386
            {
 
387
                wxPluralFormsToken::Number number = node(1)->evaluate(n);
 
388
                if (number != 0)
 
389
                {
 
390
                    return node(0)->evaluate(n) % number;
 
391
                }
 
392
                else
 
393
                {
 
394
                    return 0;
 
395
                }
 
396
            }
 
397
        case wxPluralFormsToken::T_LOGICAL_AND:
 
398
            return node(0)->evaluate(n) && node(1)->evaluate(n);
 
399
        case wxPluralFormsToken::T_LOGICAL_OR:
 
400
            return node(0)->evaluate(n) || node(1)->evaluate(n);
 
401
        // 3 args
 
402
        case wxPluralFormsToken::T_QUESTION:
 
403
            return node(0)->evaluate(n)
 
404
                ? node(1)->evaluate(n)
 
405
                : node(2)->evaluate(n);
 
406
        default:
 
407
            return 0;
 
408
    }
 
409
}
 
410
 
 
411
 
 
412
class wxPluralFormsCalculator
 
413
{
 
414
public:
 
415
    wxPluralFormsCalculator() : m_nplurals(0), m_plural(0) {}
 
416
 
 
417
    // input: number, returns msgstr index
 
418
    int evaluate(int n) const;
 
419
 
 
420
    // input: text after "Plural-Forms:" (e.g. "nplurals=2; plural=(n != 1);"),
 
421
    // if s == 0, creates default handler
 
422
    // returns 0 if error
 
423
    static wxPluralFormsCalculator* make(const char* s = 0);
 
424
 
 
425
    ~wxPluralFormsCalculator() {}
 
426
 
 
427
    void  init(wxPluralFormsToken::Number nplurals, wxPluralFormsNode* plural);
 
428
 
 
429
private:
 
430
    wxPluralFormsToken::Number m_nplurals;
 
431
    wxPluralFormsNodePtr m_plural;
 
432
};
 
433
 
 
434
wxDEFINE_SCOPED_PTR(wxPluralFormsCalculator, wxPluralFormsCalculatorPtr)
 
435
 
 
436
void wxPluralFormsCalculator::init(wxPluralFormsToken::Number nplurals,
 
437
                                wxPluralFormsNode* plural)
 
438
{
 
439
    m_nplurals = nplurals;
 
440
    m_plural.reset(plural);
 
441
}
 
442
 
 
443
int wxPluralFormsCalculator::evaluate(int n) const
 
444
{
 
445
    if (m_plural.get() == 0)
 
446
    {
 
447
        return 0;
 
448
    }
 
449
    wxPluralFormsToken::Number number = m_plural->evaluate(n);
 
450
    if (number < 0 || number > m_nplurals)
 
451
    {
 
452
        return 0;
 
453
    }
 
454
    return number;
 
455
}
 
456
 
 
457
 
 
458
class wxPluralFormsParser
 
459
{
 
460
public:
 
461
    wxPluralFormsParser(wxPluralFormsScanner& scanner) : m_scanner(scanner) {}
 
462
    bool parse(wxPluralFormsCalculator& rCalculator);
 
463
 
 
464
private:
 
465
    wxPluralFormsNode* parsePlural();
 
466
    // stops at T_SEMICOLON, returns 0 if error
 
467
    wxPluralFormsScanner& m_scanner;
 
468
    const wxPluralFormsToken& token() const;
 
469
    bool nextToken();
 
470
 
 
471
    wxPluralFormsNode* expression();
 
472
    wxPluralFormsNode* logicalOrExpression();
 
473
    wxPluralFormsNode* logicalAndExpression();
 
474
    wxPluralFormsNode* equalityExpression();
 
475
    wxPluralFormsNode* multiplicativeExpression();
 
476
    wxPluralFormsNode* relationalExpression();
 
477
    wxPluralFormsNode* pmExpression();
 
478
};
 
479
 
 
480
bool wxPluralFormsParser::parse(wxPluralFormsCalculator& rCalculator)
 
481
{
 
482
    if (token().type() != wxPluralFormsToken::T_NPLURALS)
 
483
        return false;
 
484
    if (!nextToken())
 
485
        return false;
 
486
    if (token().type() != wxPluralFormsToken::T_ASSIGN)
 
487
        return false;
 
488
    if (!nextToken())
 
489
        return false;
 
490
    if (token().type() != wxPluralFormsToken::T_NUMBER)
 
491
        return false;
 
492
    wxPluralFormsToken::Number nplurals = token().number();
 
493
    if (!nextToken())
 
494
        return false;
 
495
    if (token().type() != wxPluralFormsToken::T_SEMICOLON)
 
496
        return false;
 
497
    if (!nextToken())
 
498
        return false;
 
499
    if (token().type() != wxPluralFormsToken::T_PLURAL)
 
500
        return false;
 
501
    if (!nextToken())
 
502
        return false;
 
503
    if (token().type() != wxPluralFormsToken::T_ASSIGN)
 
504
        return false;
 
505
    if (!nextToken())
 
506
        return false;
 
507
    wxPluralFormsNode* plural = parsePlural();
 
508
    if (plural == 0)
 
509
        return false;
 
510
    if (token().type() != wxPluralFormsToken::T_SEMICOLON)
 
511
        return false;
 
512
    if (!nextToken())
 
513
        return false;
 
514
    if (token().type() != wxPluralFormsToken::T_EOF)
 
515
        return false;
 
516
    rCalculator.init(nplurals, plural);
 
517
    return true;
 
518
}
 
519
 
 
520
wxPluralFormsNode* wxPluralFormsParser::parsePlural()
 
521
{
 
522
    wxPluralFormsNode* p = expression();
 
523
    if (p == NULL)
 
524
    {
 
525
        return NULL;
 
526
    }
 
527
    wxPluralFormsNodePtr n(p);
 
528
    if (token().type() != wxPluralFormsToken::T_SEMICOLON)
 
529
    {
 
530
        return NULL;
 
531
    }
 
532
    return n.release();
 
533
}
 
534
 
 
535
const wxPluralFormsToken& wxPluralFormsParser::token() const
 
536
{
 
537
    return m_scanner.token();
 
538
}
 
539
 
 
540
bool wxPluralFormsParser::nextToken()
 
541
{
 
542
    if (!m_scanner.nextToken())
 
543
        return false;
 
544
    return true;
 
545
}
 
546
 
 
547
wxPluralFormsNode* wxPluralFormsParser::expression()
 
548
{
 
549
    wxPluralFormsNode* p = logicalOrExpression();
 
550
    if (p == NULL)
 
551
        return NULL;
 
552
    wxPluralFormsNodePtr n(p);
 
553
    if (token().type() == wxPluralFormsToken::T_QUESTION)
 
554
    {
 
555
        wxPluralFormsNodePtr qn(new wxPluralFormsNode(token()));
 
556
        if (!nextToken())
 
557
        {
 
558
            return 0;
 
559
        }
 
560
        p = expression();
 
561
        if (p == 0)
 
562
        {
 
563
            return 0;
 
564
        }
 
565
        qn->setNode(1, p);
 
566
        if (token().type() != wxPluralFormsToken::T_COLON)
 
567
        {
 
568
            return 0;
 
569
        }
 
570
        if (!nextToken())
 
571
        {
 
572
            return 0;
 
573
        }
 
574
        p = expression();
 
575
        if (p == 0)
 
576
        {
 
577
            return 0;
 
578
        }
 
579
        qn->setNode(2, p);
 
580
        qn->setNode(0, n.release());
 
581
        return qn.release();
 
582
    }
 
583
    return n.release();
 
584
}
 
585
 
 
586
wxPluralFormsNode*wxPluralFormsParser::logicalOrExpression()
 
587
{
 
588
    wxPluralFormsNode* p = logicalAndExpression();
 
589
    if (p == NULL)
 
590
        return NULL;
 
591
    wxPluralFormsNodePtr ln(p);
 
592
    if (token().type() == wxPluralFormsToken::T_LOGICAL_OR)
 
593
    {
 
594
        wxPluralFormsNodePtr un(new wxPluralFormsNode(token()));
 
595
        if (!nextToken())
 
596
        {
 
597
            return 0;
 
598
        }
 
599
        p = logicalOrExpression();
 
600
        if (p == 0)
 
601
        {
 
602
            return 0;
 
603
        }
 
604
        wxPluralFormsNodePtr rn(p);    // right
 
605
        if (rn->token().type() == wxPluralFormsToken::T_LOGICAL_OR)
 
606
        {
 
607
            // see logicalAndExpression comment
 
608
            un->setNode(0, ln.release());
 
609
            un->setNode(1, rn->releaseNode(0));
 
610
            rn->setNode(0, un.release());
 
611
            return rn.release();
 
612
        }
 
613
 
 
614
 
 
615
        un->setNode(0, ln.release());
 
616
        un->setNode(1, rn.release());
 
617
        return un.release();
 
618
    }
 
619
    return ln.release();
 
620
}
 
621
 
 
622
wxPluralFormsNode* wxPluralFormsParser::logicalAndExpression()
 
623
{
 
624
    wxPluralFormsNode* p = equalityExpression();
 
625
    if (p == NULL)
 
626
        return NULL;
 
627
    wxPluralFormsNodePtr ln(p);   // left
 
628
    if (token().type() == wxPluralFormsToken::T_LOGICAL_AND)
 
629
    {
 
630
        wxPluralFormsNodePtr un(new wxPluralFormsNode(token()));  // up
 
631
        if (!nextToken())
 
632
        {
 
633
            return NULL;
 
634
        }
 
635
        p = logicalAndExpression();
 
636
        if (p == 0)
 
637
        {
 
638
            return NULL;
 
639
        }
 
640
        wxPluralFormsNodePtr rn(p);    // right
 
641
        if (rn->token().type() == wxPluralFormsToken::T_LOGICAL_AND)
 
642
        {
 
643
// transform 1 && (2 && 3) -> (1 && 2) && 3
 
644
//     u                  r
 
645
// l       r     ->   u      3
 
646
//       2   3      l   2
 
647
            un->setNode(0, ln.release());
 
648
            un->setNode(1, rn->releaseNode(0));
 
649
            rn->setNode(0, un.release());
 
650
            return rn.release();
 
651
        }
 
652
 
 
653
        un->setNode(0, ln.release());
 
654
        un->setNode(1, rn.release());
 
655
        return un.release();
 
656
    }
 
657
    return ln.release();
 
658
}
 
659
 
 
660
wxPluralFormsNode* wxPluralFormsParser::equalityExpression()
 
661
{
 
662
    wxPluralFormsNode* p = relationalExpression();
 
663
    if (p == NULL)
 
664
        return NULL;
 
665
    wxPluralFormsNodePtr n(p);
 
666
    if (token().type() == wxPluralFormsToken::T_EQUAL
 
667
        || token().type() == wxPluralFormsToken::T_NOT_EQUAL)
 
668
    {
 
669
        wxPluralFormsNodePtr qn(new wxPluralFormsNode(token()));
 
670
        if (!nextToken())
 
671
        {
 
672
            return NULL;
 
673
        }
 
674
        p = relationalExpression();
 
675
        if (p == NULL)
 
676
        {
 
677
            return NULL;
 
678
        }
 
679
        qn->setNode(1, p);
 
680
        qn->setNode(0, n.release());
 
681
        return qn.release();
 
682
    }
 
683
    return n.release();
 
684
}
 
685
 
 
686
wxPluralFormsNode* wxPluralFormsParser::relationalExpression()
 
687
{
 
688
    wxPluralFormsNode* p = multiplicativeExpression();
 
689
    if (p == NULL)
 
690
        return NULL;
 
691
    wxPluralFormsNodePtr n(p);
 
692
    if (token().type() == wxPluralFormsToken::T_GREATER
 
693
            || token().type() == wxPluralFormsToken::T_LESS
 
694
            || token().type() == wxPluralFormsToken::T_GREATER_OR_EQUAL
 
695
            || token().type() == wxPluralFormsToken::T_LESS_OR_EQUAL)
 
696
    {
 
697
        wxPluralFormsNodePtr qn(new wxPluralFormsNode(token()));
 
698
        if (!nextToken())
 
699
        {
 
700
            return NULL;
 
701
        }
 
702
        p = multiplicativeExpression();
 
703
        if (p == NULL)
 
704
        {
 
705
            return NULL;
 
706
        }
 
707
        qn->setNode(1, p);
 
708
        qn->setNode(0, n.release());
 
709
        return qn.release();
 
710
    }
 
711
    return n.release();
 
712
}
 
713
 
 
714
wxPluralFormsNode* wxPluralFormsParser::multiplicativeExpression()
 
715
{
 
716
    wxPluralFormsNode* p = pmExpression();
 
717
    if (p == NULL)
 
718
        return NULL;
 
719
    wxPluralFormsNodePtr n(p);
 
720
    if (token().type() == wxPluralFormsToken::T_REMINDER)
 
721
    {
 
722
        wxPluralFormsNodePtr qn(new wxPluralFormsNode(token()));
 
723
        if (!nextToken())
 
724
        {
 
725
            return NULL;
 
726
        }
 
727
        p = pmExpression();
 
728
        if (p == NULL)
 
729
        {
 
730
            return NULL;
 
731
        }
 
732
        qn->setNode(1, p);
 
733
        qn->setNode(0, n.release());
 
734
        return qn.release();
 
735
    }
 
736
    return n.release();
 
737
}
 
738
 
 
739
wxPluralFormsNode* wxPluralFormsParser::pmExpression()
 
740
{
 
741
    wxPluralFormsNodePtr n;
 
742
    if (token().type() == wxPluralFormsToken::T_N
 
743
        || token().type() == wxPluralFormsToken::T_NUMBER)
 
744
    {
 
745
        n.reset(new wxPluralFormsNode(token()));
 
746
        if (!nextToken())
 
747
        {
 
748
            return NULL;
 
749
        }
 
750
    }
 
751
    else if (token().type() == wxPluralFormsToken::T_LEFT_BRACKET) {
 
752
        if (!nextToken())
 
753
        {
 
754
            return NULL;
 
755
        }
 
756
        wxPluralFormsNode* p = expression();
 
757
        if (p == NULL)
 
758
        {
 
759
            return NULL;
 
760
        }
 
761
        n.reset(p);
 
762
        if (token().type() != wxPluralFormsToken::T_RIGHT_BRACKET)
 
763
        {
 
764
            return NULL;
 
765
        }
 
766
        if (!nextToken())
 
767
        {
 
768
            return NULL;
 
769
        }
 
770
    }
 
771
    else
 
772
    {
 
773
        return NULL;
 
774
    }
 
775
    return n.release();
 
776
}
 
777
 
 
778
wxPluralFormsCalculator* wxPluralFormsCalculator::make(const char* s)
 
779
{
 
780
    wxPluralFormsCalculatorPtr calculator(new wxPluralFormsCalculator);
 
781
    if (s != NULL)
 
782
    {
 
783
        wxPluralFormsScanner scanner(s);
 
784
        wxPluralFormsParser p(scanner);
 
785
        if (!p.parse(*calculator))
 
786
        {
 
787
            return NULL;
 
788
        }
 
789
    }
 
790
    return calculator.release();
 
791
}
 
792
 
 
793
 
 
794
 
 
795
 
 
796
// ----------------------------------------------------------------------------
 
797
// wxMsgCatalogFile corresponds to one disk-file message catalog.
 
798
//
 
799
// This is a "low-level" class and is used only by wxMsgCatalog
 
800
// NOTE: for the documentation of the binary catalog (.MO) files refer to
 
801
//       the GNU gettext manual:
 
802
//       http://www.gnu.org/software/autoconf/manual/gettext/MO-Files.html
 
803
// ----------------------------------------------------------------------------
 
804
 
 
805
class wxMsgCatalogFile
 
806
{
 
807
public:
 
808
    typedef wxScopedCharBuffer DataBuffer;
 
809
 
 
810
    // ctor & dtor
 
811
    wxMsgCatalogFile();
 
812
    ~wxMsgCatalogFile();
 
813
 
 
814
    // load the catalog from disk
 
815
    bool LoadFile(const wxString& filename,
 
816
                  wxPluralFormsCalculatorPtr& rPluralFormsCalculator);
 
817
    bool LoadData(const DataBuffer& data,
 
818
                  wxPluralFormsCalculatorPtr& rPluralFormsCalculator);
 
819
 
 
820
    // fills the hash with string-translation pairs
 
821
    bool FillHash(wxStringToStringHashMap& hash, const wxString& domain) const;
 
822
 
 
823
    // return the charset of the strings in this catalog or empty string if
 
824
    // none/unknown
 
825
    wxString GetCharset() const { return m_charset; }
 
826
 
 
827
private:
 
828
    // this implementation is binary compatible with GNU gettext() version 0.10
 
829
 
 
830
    // an entry in the string table
 
831
    struct wxMsgTableEntry
 
832
    {
 
833
        size_t32   nLen;           // length of the string
 
834
        size_t32   ofsString;      // pointer to the string
 
835
    };
 
836
 
 
837
    // header of a .mo file
 
838
    struct wxMsgCatalogHeader
 
839
    {
 
840
        size_t32  magic,          // offset +00:  magic id
 
841
                  revision,       //        +04:  revision
 
842
                  numStrings;     //        +08:  number of strings in the file
 
843
        size_t32  ofsOrigTable,   //        +0C:  start of original string table
 
844
                  ofsTransTable;  //        +10:  start of translated string table
 
845
        size_t32  nHashSize,      //        +14:  hash table size
 
846
                  ofsHashTable;   //        +18:  offset of hash table start
 
847
    };
 
848
 
 
849
    // all data is stored here
 
850
    DataBuffer m_data;
 
851
 
 
852
    // data description
 
853
    size_t32          m_numStrings;   // number of strings in this domain
 
854
    wxMsgTableEntry  *m_pOrigTable,   // pointer to original   strings
 
855
                     *m_pTransTable;  //            translated
 
856
 
 
857
    wxString m_charset;               // from the message catalog header
 
858
 
 
859
 
 
860
    // swap the 2 halves of 32 bit integer if needed
 
861
    size_t32 Swap(size_t32 ui) const
 
862
    {
 
863
        return m_bSwapped ? (ui << 24) | ((ui & 0xff00) << 8) |
 
864
                            ((ui >> 8) & 0xff00) | (ui >> 24)
 
865
                            : ui;
 
866
    }
 
867
 
 
868
    const char *StringAtOfs(wxMsgTableEntry *pTable, size_t32 n) const
 
869
    {
 
870
        const wxMsgTableEntry * const ent = pTable + n;
 
871
 
 
872
        // this check could fail for a corrupt message catalog
 
873
        size_t32 ofsString = Swap(ent->ofsString);
 
874
        if ( ofsString + Swap(ent->nLen) > m_data.length())
 
875
        {
 
876
            return NULL;
 
877
        }
 
878
 
 
879
        return m_data.data() + ofsString;
 
880
    }
 
881
 
 
882
    bool m_bSwapped;   // wrong endianness?
 
883
 
 
884
    wxDECLARE_NO_COPY_CLASS(wxMsgCatalogFile);
 
885
};
 
886
 
 
887
// ----------------------------------------------------------------------------
 
888
// wxMsgCatalogFile class
 
889
// ----------------------------------------------------------------------------
 
890
 
 
891
wxMsgCatalogFile::wxMsgCatalogFile()
 
892
{
 
893
}
 
894
 
 
895
wxMsgCatalogFile::~wxMsgCatalogFile()
 
896
{
 
897
}
 
898
 
 
899
// open disk file and read in it's contents
 
900
bool wxMsgCatalogFile::LoadFile(const wxString& filename,
 
901
                                wxPluralFormsCalculatorPtr& rPluralFormsCalculator)
 
902
{
 
903
    wxFile fileMsg(filename);
 
904
    if ( !fileMsg.IsOpened() )
 
905
        return false;
 
906
 
 
907
    // get the file size (assume it is less than 4GB...)
 
908
    wxFileOffset lenFile = fileMsg.Length();
 
909
    if ( lenFile == wxInvalidOffset )
 
910
        return false;
 
911
 
 
912
    size_t nSize = wx_truncate_cast(size_t, lenFile);
 
913
    wxASSERT_MSG( nSize == lenFile + size_t(0), wxS("message catalog bigger than 4GB?") );
 
914
 
 
915
    wxMemoryBuffer filedata;
 
916
 
 
917
    // read the whole file in memory
 
918
    if ( fileMsg.Read(filedata.GetWriteBuf(nSize), nSize) != lenFile )
 
919
        return false;
 
920
 
 
921
    filedata.UngetWriteBuf(nSize);
 
922
 
 
923
    bool ok = LoadData
 
924
              (
 
925
                  DataBuffer::CreateOwned((char*)filedata.release(), nSize),
 
926
                  rPluralFormsCalculator
 
927
              );
 
928
    if ( !ok )
 
929
    {
 
930
        wxLogWarning(_("'%s' is not a valid message catalog."), filename.c_str());
 
931
        return false;
 
932
    }
 
933
 
 
934
    return true;
 
935
}
 
936
 
 
937
 
 
938
bool wxMsgCatalogFile::LoadData(const DataBuffer& data,
 
939
                                wxPluralFormsCalculatorPtr& rPluralFormsCalculator)
 
940
{
 
941
    // examine header
 
942
    bool bValid = data.length() > sizeof(wxMsgCatalogHeader);
 
943
 
 
944
    const wxMsgCatalogHeader *pHeader = (wxMsgCatalogHeader *)data.data();
 
945
    if ( bValid ) {
 
946
        // we'll have to swap all the integers if it's true
 
947
        m_bSwapped = pHeader->magic == MSGCATALOG_MAGIC_SW;
 
948
 
 
949
        // check the magic number
 
950
        bValid = m_bSwapped || pHeader->magic == MSGCATALOG_MAGIC;
 
951
    }
 
952
 
 
953
    if ( !bValid ) {
 
954
        // it's either too short or has incorrect magic number
 
955
        wxLogWarning(_("Invalid message catalog."));
 
956
        return false;
 
957
    }
 
958
 
 
959
    m_data = data;
 
960
 
 
961
    // initialize
 
962
    m_numStrings  = Swap(pHeader->numStrings);
 
963
    m_pOrigTable  = (wxMsgTableEntry *)(data.data() +
 
964
                    Swap(pHeader->ofsOrigTable));
 
965
    m_pTransTable = (wxMsgTableEntry *)(data.data() +
 
966
                    Swap(pHeader->ofsTransTable));
 
967
 
 
968
    // now parse catalog's header and try to extract catalog charset and
 
969
    // plural forms formula from it:
 
970
 
 
971
    const char* headerData = StringAtOfs(m_pOrigTable, 0);
 
972
    if ( headerData && headerData[0] == '\0' )
 
973
    {
 
974
        // Extract the charset:
 
975
        const char * const header = StringAtOfs(m_pTransTable, 0);
 
976
        const char *
 
977
            cset = strstr(header, "Content-Type: text/plain; charset=");
 
978
        if ( cset )
 
979
        {
 
980
            cset += 34; // strlen("Content-Type: text/plain; charset=")
 
981
 
 
982
            const char * const csetEnd = strchr(cset, '\n');
 
983
            if ( csetEnd )
 
984
            {
 
985
                m_charset = wxString(cset, csetEnd - cset);
 
986
                if ( m_charset == wxS("CHARSET") )
 
987
                {
 
988
                    // "CHARSET" is not valid charset, but lazy translator
 
989
                    m_charset.clear();
 
990
                }
 
991
            }
 
992
        }
 
993
        // else: incorrectly filled Content-Type header
 
994
 
 
995
        // Extract plural forms:
 
996
        const char * plurals = strstr(header, "Plural-Forms:");
 
997
        if ( plurals )
 
998
        {
 
999
            plurals += 13; // strlen("Plural-Forms:")
 
1000
            const char * const pluralsEnd = strchr(plurals, '\n');
 
1001
            if ( pluralsEnd )
 
1002
            {
 
1003
                const size_t pluralsLen = pluralsEnd - plurals;
 
1004
                wxCharBuffer buf(pluralsLen);
 
1005
                strncpy(buf.data(), plurals, pluralsLen);
 
1006
                wxPluralFormsCalculator * const
 
1007
                    pCalculator = wxPluralFormsCalculator::make(buf);
 
1008
                if ( pCalculator )
 
1009
                {
 
1010
                    rPluralFormsCalculator.reset(pCalculator);
 
1011
                }
 
1012
                else
 
1013
                {
 
1014
                    wxLogVerbose(_("Failed to parse Plural-Forms: '%s'"),
 
1015
                                 buf.data());
 
1016
                }
 
1017
            }
 
1018
        }
 
1019
 
 
1020
        if ( !rPluralFormsCalculator.get() )
 
1021
            rPluralFormsCalculator.reset(wxPluralFormsCalculator::make());
 
1022
    }
 
1023
 
 
1024
    // everything is fine
 
1025
    return true;
 
1026
}
 
1027
 
 
1028
bool wxMsgCatalogFile::FillHash(wxStringToStringHashMap& hash,
 
1029
                                const wxString& domain) const
 
1030
{
 
1031
    wxUnusedVar(domain); // silence warning in Unicode build
 
1032
 
 
1033
    // conversion to use to convert catalog strings to the GUI encoding
 
1034
    wxMBConv *inputConv = NULL;
 
1035
    wxMBConv *inputConvPtr = NULL; // same as inputConv but safely deleteable
 
1036
 
 
1037
    if ( !m_charset.empty() )
 
1038
    {
 
1039
#if !wxUSE_UNICODE && wxUSE_FONTMAP
 
1040
        // determine if we need any conversion at all
 
1041
        wxFontEncoding encCat = wxFontMapperBase::GetEncodingFromName(m_charset);
 
1042
        if ( encCat != wxLocale::GetSystemEncoding() )
 
1043
#endif
 
1044
        {
 
1045
            inputConvPtr =
 
1046
            inputConv = new wxCSConv(m_charset);
 
1047
        }
 
1048
    }
 
1049
    else // no need or not possible to convert the encoding
 
1050
    {
 
1051
#if wxUSE_UNICODE
 
1052
        // we must somehow convert the narrow strings in the message catalog to
 
1053
        // wide strings, so use the default conversion if we have no charset
 
1054
        inputConv = wxConvCurrent;
 
1055
#endif
 
1056
    }
 
1057
 
 
1058
#if !wxUSE_UNICODE
 
1059
    wxString msgIdCharset = gs_msgIdCharset[domain];
 
1060
 
 
1061
    // conversion to apply to msgid strings before looking them up: we only
 
1062
    // need it if the msgids are neither in 7 bit ASCII nor in the same
 
1063
    // encoding as the catalog
 
1064
    wxCSConv *sourceConv = msgIdCharset.empty() || (msgIdCharset == m_charset)
 
1065
                            ? NULL
 
1066
                            : new wxCSConv(msgIdCharset);
 
1067
#endif // !wxUSE_UNICODE
 
1068
 
 
1069
    for (size_t32 i = 0; i < m_numStrings; i++)
 
1070
    {
 
1071
        const char *data = StringAtOfs(m_pOrigTable, i);
 
1072
        if (!data)
 
1073
            return false; // may happen for invalid MO files
 
1074
 
 
1075
        wxString msgid;
 
1076
#if wxUSE_UNICODE
 
1077
        msgid = wxString(data, *inputConv);
 
1078
#else // ASCII
 
1079
        if ( inputConv && sourceConv )
 
1080
            msgid = wxString(inputConv->cMB2WC(data), *sourceConv);
 
1081
        else
 
1082
            msgid = data;
 
1083
#endif // wxUSE_UNICODE
 
1084
 
 
1085
        data = StringAtOfs(m_pTransTable, i);
 
1086
        if (!data)
 
1087
            return false; // may happen for invalid MO files
 
1088
 
 
1089
        size_t length = Swap(m_pTransTable[i].nLen);
 
1090
        size_t offset = 0;
 
1091
        size_t index = 0;
 
1092
        while (offset < length)
 
1093
        {
 
1094
            const char * const str = data + offset;
 
1095
 
 
1096
            wxString msgstr;
 
1097
#if wxUSE_UNICODE
 
1098
            msgstr = wxString(str, *inputConv);
 
1099
#else
 
1100
            if ( inputConv )
 
1101
                msgstr = wxString(inputConv->cMB2WC(str), *wxConvUI);
 
1102
            else
 
1103
                msgstr = str;
 
1104
#endif // wxUSE_UNICODE/!wxUSE_UNICODE
 
1105
 
 
1106
            if ( !msgstr.empty() )
 
1107
            {
 
1108
                hash[index == 0 ? msgid : msgid + wxChar(index)] = msgstr;
 
1109
            }
 
1110
 
 
1111
            // skip this string
 
1112
            // IMPORTANT: accesses to the 'data' pointer are valid only for
 
1113
            //            the first 'length+1' bytes (GNU specs says that the
 
1114
            //            final NUL is not counted in length); using wxStrnlen()
 
1115
            //            we make sure we don't access memory beyond the valid range
 
1116
            //            (which otherwise may happen for invalid MO files):
 
1117
            offset += wxStrnlen(str, length - offset) + 1;
 
1118
            ++index;
 
1119
        }
 
1120
    }
 
1121
 
 
1122
#if !wxUSE_UNICODE
 
1123
    delete sourceConv;
 
1124
#endif
 
1125
    delete inputConvPtr;
 
1126
 
 
1127
    return true;
 
1128
}
 
1129
 
 
1130
 
 
1131
// ----------------------------------------------------------------------------
 
1132
// wxMsgCatalog class
 
1133
// ----------------------------------------------------------------------------
 
1134
 
 
1135
#if !wxUSE_UNICODE
 
1136
wxMsgCatalog::~wxMsgCatalog()
 
1137
{
 
1138
    if ( m_conv )
 
1139
    {
 
1140
        if ( wxConvUI == m_conv )
 
1141
        {
 
1142
            // we only change wxConvUI if it points to wxConvLocal so we reset
 
1143
            // it back to it too
 
1144
            wxConvUI = &wxConvLocal;
 
1145
        }
 
1146
 
 
1147
        delete m_conv;
 
1148
    }
 
1149
}
 
1150
#endif // !wxUSE_UNICODE
 
1151
 
 
1152
/* static */
 
1153
wxMsgCatalog *wxMsgCatalog::CreateFromFile(const wxString& filename,
 
1154
                                           const wxString& domain)
 
1155
{
 
1156
    wxScopedPtr<wxMsgCatalog> cat(new wxMsgCatalog(domain));
 
1157
 
 
1158
    wxMsgCatalogFile file;
 
1159
 
 
1160
    if ( !file.LoadFile(filename, cat->m_pluralFormsCalculator) )
 
1161
        return NULL;
 
1162
 
 
1163
    if ( !file.FillHash(cat->m_messages, domain) )
 
1164
        return NULL;
 
1165
 
 
1166
    return cat.release();
 
1167
}
 
1168
 
 
1169
/* static */
 
1170
wxMsgCatalog *wxMsgCatalog::CreateFromData(const wxScopedCharBuffer& data,
 
1171
                                           const wxString& domain)
 
1172
{
 
1173
    wxScopedPtr<wxMsgCatalog> cat(new wxMsgCatalog(domain));
 
1174
 
 
1175
    wxMsgCatalogFile file;
 
1176
 
 
1177
    if ( !file.LoadData(data, cat->m_pluralFormsCalculator) )
 
1178
        return NULL;
 
1179
 
 
1180
    if ( !file.FillHash(cat->m_messages, domain) )
 
1181
        return NULL;
 
1182
 
 
1183
    return cat.release();
 
1184
}
 
1185
 
 
1186
const wxString *wxMsgCatalog::GetString(const wxString& str, unsigned n) const
 
1187
{
 
1188
    int index = 0;
 
1189
    if (n != UINT_MAX)
 
1190
    {
 
1191
        index = m_pluralFormsCalculator->evaluate(n);
 
1192
    }
 
1193
    wxStringToStringHashMap::const_iterator i;
 
1194
    if (index != 0)
 
1195
    {
 
1196
        i = m_messages.find(wxString(str) + wxChar(index));   // plural
 
1197
    }
 
1198
    else
 
1199
    {
 
1200
        i = m_messages.find(str);
 
1201
    }
 
1202
 
 
1203
    if ( i != m_messages.end() )
 
1204
    {
 
1205
        return &i->second;
 
1206
    }
 
1207
    else
 
1208
        return NULL;
 
1209
}
 
1210
 
 
1211
 
 
1212
// ----------------------------------------------------------------------------
 
1213
// wxTranslations
 
1214
// ----------------------------------------------------------------------------
 
1215
 
 
1216
namespace
 
1217
{
 
1218
 
 
1219
wxTranslations *gs_translations = NULL;
 
1220
bool gs_translationsOwned = false;
 
1221
 
 
1222
} // anonymous namespace
 
1223
 
 
1224
 
 
1225
/*static*/
 
1226
wxTranslations *wxTranslations::Get()
 
1227
{
 
1228
    return gs_translations;
 
1229
}
 
1230
 
 
1231
/*static*/
 
1232
void wxTranslations::Set(wxTranslations *t)
 
1233
{
 
1234
    if ( gs_translationsOwned )
 
1235
        delete gs_translations;
 
1236
    gs_translations = t;
 
1237
    gs_translationsOwned = true;
 
1238
}
 
1239
 
 
1240
/*static*/
 
1241
void wxTranslations::SetNonOwned(wxTranslations *t)
 
1242
{
 
1243
    if ( gs_translationsOwned )
 
1244
        delete gs_translations;
 
1245
    gs_translations = t;
 
1246
    gs_translationsOwned = false;
 
1247
}
 
1248
 
 
1249
 
 
1250
wxTranslations::wxTranslations()
 
1251
{
 
1252
    m_pMsgCat = NULL;
 
1253
    m_loader = new wxFileTranslationsLoader;
 
1254
}
 
1255
 
 
1256
 
 
1257
wxTranslations::~wxTranslations()
 
1258
{
 
1259
    delete m_loader;
 
1260
 
 
1261
    // free catalogs memory
 
1262
    wxMsgCatalog *pTmpCat;
 
1263
    while ( m_pMsgCat != NULL )
 
1264
    {
 
1265
        pTmpCat = m_pMsgCat;
 
1266
        m_pMsgCat = m_pMsgCat->m_pNext;
 
1267
        delete pTmpCat;
 
1268
    }
 
1269
}
 
1270
 
 
1271
 
 
1272
void wxTranslations::SetLoader(wxTranslationsLoader *loader)
 
1273
{
 
1274
    wxCHECK_RET( loader, "loader can't be NULL" );
 
1275
 
 
1276
    delete m_loader;
 
1277
    m_loader = loader;
 
1278
}
 
1279
 
 
1280
 
 
1281
void wxTranslations::SetLanguage(wxLanguage lang)
 
1282
{
 
1283
    if ( lang == wxLANGUAGE_DEFAULT )
 
1284
        SetLanguage("");
 
1285
    else
 
1286
        SetLanguage(wxLocale::GetLanguageCanonicalName(lang));
 
1287
}
 
1288
 
 
1289
void wxTranslations::SetLanguage(const wxString& lang)
 
1290
{
 
1291
    m_lang = lang;
 
1292
}
 
1293
 
 
1294
 
 
1295
wxArrayString wxTranslations::GetAvailableTranslations(const wxString& domain) const
 
1296
{
 
1297
    wxCHECK_MSG( m_loader, wxArrayString(), "loader can't be NULL" );
 
1298
 
 
1299
    return m_loader->GetAvailableTranslations(domain);
 
1300
}
 
1301
 
 
1302
 
 
1303
bool wxTranslations::AddStdCatalog()
 
1304
{
 
1305
    if ( !AddCatalog(wxS("wxstd")) )
 
1306
        return false;
 
1307
 
 
1308
    // there may be a catalog with toolkit specific overrides, it is not
 
1309
    // an error if this does not exist
 
1310
    wxString port(wxPlatformInfo::Get().GetPortIdName());
 
1311
    if ( !port.empty() )
 
1312
    {
 
1313
        AddCatalog(port.BeforeFirst(wxS('/')).MakeLower());
 
1314
    }
 
1315
 
 
1316
    return true;
 
1317
}
 
1318
 
 
1319
 
 
1320
bool wxTranslations::AddCatalog(const wxString& domain)
 
1321
{
 
1322
    return AddCatalog(domain, wxLANGUAGE_ENGLISH_US);
 
1323
}
 
1324
 
 
1325
#if !wxUSE_UNICODE
 
1326
bool wxTranslations::AddCatalog(const wxString& domain,
 
1327
                                wxLanguage msgIdLanguage,
 
1328
                                const wxString& msgIdCharset)
 
1329
{
 
1330
    gs_msgIdCharset[domain] = msgIdCharset;
 
1331
    return AddCatalog(domain, msgIdLanguage);
 
1332
}
 
1333
#endif // !wxUSE_UNICODE
 
1334
 
 
1335
bool wxTranslations::AddCatalog(const wxString& domain,
 
1336
                                wxLanguage msgIdLanguage)
 
1337
{
 
1338
    const wxString msgIdLang = wxLocale::GetLanguageCanonicalName(msgIdLanguage);
 
1339
    const wxString domain_lang = ChooseLanguageForDomain(domain, msgIdLang);
 
1340
 
 
1341
    if ( domain_lang.empty() )
 
1342
    {
 
1343
        wxLogTrace(TRACE_I18N,
 
1344
                    wxS("no suitable translation for domain '%s' found"),
 
1345
                    domain);
 
1346
        return false;
 
1347
    }
 
1348
 
 
1349
    wxLogTrace(TRACE_I18N,
 
1350
                wxS("adding '%s' translation for domain '%s' (msgid language '%s')"),
 
1351
                domain_lang, domain, msgIdLang);
 
1352
 
 
1353
    // It is OK to not load catalog if the msgid language and m_language match,
 
1354
    // in which case we can directly display the texts embedded in program's
 
1355
    // source code:
 
1356
    if ( msgIdLang == domain_lang )
 
1357
        return true;
 
1358
 
 
1359
    return LoadCatalog(domain, domain_lang);
 
1360
}
 
1361
 
 
1362
 
 
1363
bool wxTranslations::LoadCatalog(const wxString& domain, const wxString& lang)
 
1364
{
 
1365
    m_loader->GetAvailableTranslations(domain);
 
1366
    wxCHECK_MSG( m_loader, false, "loader can't be NULL" );
 
1367
 
 
1368
    wxMsgCatalog *cat = NULL;
 
1369
 
 
1370
#if wxUSE_FONTMAP
 
1371
    // first look for the catalog for this language and the current locale:
 
1372
    // notice that we don't use the system name for the locale as this would
 
1373
    // force us to install catalogs in different locations depending on the
 
1374
    // system but always use the canonical name
 
1375
    wxFontEncoding encSys = wxLocale::GetSystemEncoding();
 
1376
    if ( encSys != wxFONTENCODING_SYSTEM )
 
1377
    {
 
1378
        wxString fullname(lang);
 
1379
        fullname << wxS('.') << wxFontMapperBase::GetEncodingName(encSys);
 
1380
 
 
1381
        cat = m_loader->LoadCatalog(domain, fullname);
 
1382
    }
 
1383
#endif // wxUSE_FONTMAP
 
1384
 
 
1385
    if ( !cat )
 
1386
    {
 
1387
        // Next try: use the provided name language name:
 
1388
        cat = m_loader->LoadCatalog(domain, lang);
 
1389
    }
 
1390
 
 
1391
    if ( !cat )
 
1392
    {
 
1393
        // Also try just base locale name: for things like "fr_BE" (Belgium
 
1394
        // French) we should use fall back on plain "fr" if no Belgium-specific
 
1395
        // message catalogs exist
 
1396
        wxString baselang = lang.BeforeFirst('_');
 
1397
        if ( lang != baselang )
 
1398
            cat = m_loader->LoadCatalog(domain, baselang);
 
1399
    }
 
1400
 
 
1401
    if ( cat )
 
1402
    {
 
1403
        // add it to the head of the list so that in GetString it will
 
1404
        // be searched before the catalogs added earlier
 
1405
        cat->m_pNext = m_pMsgCat;
 
1406
        m_pMsgCat = cat;
 
1407
 
 
1408
        return true;
 
1409
    }
 
1410
    else
 
1411
    {
 
1412
        // Nothing worked, the catalog just isn't there
 
1413
        wxLogTrace(TRACE_I18N,
 
1414
                   "Catalog \"%s.mo\" not found for language \"%s\".",
 
1415
                   domain, lang);
 
1416
        return false;
 
1417
    }
 
1418
}
 
1419
 
 
1420
// check if the given catalog is loaded
 
1421
bool wxTranslations::IsLoaded(const wxString& domain) const
 
1422
{
 
1423
    return FindCatalog(domain) != NULL;
 
1424
}
 
1425
 
 
1426
 
 
1427
wxString wxTranslations::ChooseLanguageForDomain(const wxString& WXUNUSED(domain),
 
1428
                                                 const wxString& WXUNUSED(msgIdLang))
 
1429
{
 
1430
    // explicitly set language should always be respected
 
1431
    if ( !m_lang.empty() )
 
1432
        return m_lang;
 
1433
 
 
1434
    // TODO: if the default language is used, pick the best (by comparing
 
1435
    //       available languages with user's preferences), instead of blindly
 
1436
    //       trusting availability of system language translation
 
1437
    return wxLocale::GetLanguageCanonicalName(wxLocale::GetSystemLanguage());
 
1438
}
 
1439
 
 
1440
 
 
1441
namespace
 
1442
{
 
1443
WX_DECLARE_HASH_SET(wxString, ::wxStringHash, ::wxStringEqual,
 
1444
                    wxLocaleUntranslatedStrings);
 
1445
}
 
1446
 
 
1447
/* static */
 
1448
const wxString& wxTranslations::GetUntranslatedString(const wxString& str)
 
1449
{
 
1450
    static wxLocaleUntranslatedStrings s_strings;
 
1451
 
 
1452
    wxLocaleUntranslatedStrings::iterator i = s_strings.find(str);
 
1453
    if ( i == s_strings.end() )
 
1454
        return *s_strings.insert(str).first;
 
1455
 
 
1456
    return *i;
 
1457
}
 
1458
 
 
1459
 
 
1460
const wxString& wxTranslations::GetString(const wxString& origString,
 
1461
                                          const wxString& domain) const
 
1462
{
 
1463
    return GetString(origString, origString, UINT_MAX, domain);
 
1464
}
 
1465
 
 
1466
const wxString& wxTranslations::GetString(const wxString& origString,
 
1467
                                          const wxString& origString2,
 
1468
                                          unsigned n,
 
1469
                                          const wxString& domain) const
 
1470
{
 
1471
    if ( origString.empty() )
 
1472
        return GetUntranslatedString(origString);
 
1473
 
 
1474
    const wxString *trans = NULL;
 
1475
    wxMsgCatalog *pMsgCat;
 
1476
 
 
1477
    if ( !domain.empty() )
 
1478
    {
 
1479
        pMsgCat = FindCatalog(domain);
 
1480
 
 
1481
        // does the catalog exist?
 
1482
        if ( pMsgCat != NULL )
 
1483
            trans = pMsgCat->GetString(origString, n);
 
1484
    }
 
1485
    else
 
1486
    {
 
1487
        // search in all domains
 
1488
        for ( pMsgCat = m_pMsgCat; pMsgCat != NULL; pMsgCat = pMsgCat->m_pNext )
 
1489
        {
 
1490
            trans = pMsgCat->GetString(origString, n);
 
1491
            if ( trans != NULL )   // take the first found
 
1492
                break;
 
1493
        }
 
1494
    }
 
1495
 
 
1496
    if ( trans == NULL )
 
1497
    {
 
1498
        wxLogTrace
 
1499
        (
 
1500
            TRACE_I18N,
 
1501
            "string \"%s\"%s not found in %slocale '%s'.",
 
1502
            origString,
 
1503
            (n != UINT_MAX ? wxString::Format("[%ld]", (long)n) : wxString()),
 
1504
            (!domain.empty() ? wxString::Format("domain '%s' ", domain) : wxString()),
 
1505
            m_lang
 
1506
        );
 
1507
 
 
1508
        if (n == UINT_MAX)
 
1509
            return GetUntranslatedString(origString);
 
1510
        else
 
1511
            return GetUntranslatedString(n == 1 ? origString : origString2);
 
1512
    }
 
1513
 
 
1514
    return *trans;
 
1515
}
 
1516
 
 
1517
 
 
1518
wxString wxTranslations::GetHeaderValue(const wxString& header,
 
1519
                                        const wxString& domain) const
 
1520
{
 
1521
    if ( header.empty() )
 
1522
        return wxEmptyString;
 
1523
 
 
1524
    const wxString *trans = NULL;
 
1525
    wxMsgCatalog *pMsgCat;
 
1526
 
 
1527
    if ( !domain.empty() )
 
1528
    {
 
1529
        pMsgCat = FindCatalog(domain);
 
1530
 
 
1531
        // does the catalog exist?
 
1532
        if ( pMsgCat == NULL )
 
1533
            return wxEmptyString;
 
1534
 
 
1535
        trans = pMsgCat->GetString(wxEmptyString, UINT_MAX);
 
1536
    }
 
1537
    else
 
1538
    {
 
1539
        // search in all domains
 
1540
        for ( pMsgCat = m_pMsgCat; pMsgCat != NULL; pMsgCat = pMsgCat->m_pNext )
 
1541
        {
 
1542
            trans = pMsgCat->GetString(wxEmptyString, UINT_MAX);
 
1543
            if ( trans != NULL )   // take the first found
 
1544
                break;
 
1545
        }
 
1546
    }
 
1547
 
 
1548
    if ( !trans || trans->empty() )
 
1549
        return wxEmptyString;
 
1550
 
 
1551
    size_t found = trans->find(header);
 
1552
    if ( found == wxString::npos )
 
1553
        return wxEmptyString;
 
1554
 
 
1555
    found += header.length() + 2 /* ': ' */;
 
1556
 
 
1557
    // Every header is separated by \n
 
1558
 
 
1559
    size_t endLine = trans->find(wxS('\n'), found);
 
1560
    size_t len = (endLine == wxString::npos) ?
 
1561
                wxString::npos : (endLine - found);
 
1562
 
 
1563
    return trans->substr(found, len);
 
1564
}
 
1565
 
 
1566
 
 
1567
// find catalog by name in a linked list, return NULL if !found
 
1568
wxMsgCatalog *wxTranslations::FindCatalog(const wxString& domain) const
 
1569
{
 
1570
    // linear search in the linked list
 
1571
    wxMsgCatalog *pMsgCat;
 
1572
    for ( pMsgCat = m_pMsgCat; pMsgCat != NULL; pMsgCat = pMsgCat->m_pNext )
 
1573
    {
 
1574
        if ( pMsgCat->GetDomain() == domain )
 
1575
            return pMsgCat;
 
1576
    }
 
1577
 
 
1578
    return NULL;
 
1579
}
 
1580
 
 
1581
// ----------------------------------------------------------------------------
 
1582
// wxFileTranslationsLoader
 
1583
// ----------------------------------------------------------------------------
 
1584
 
 
1585
namespace
 
1586
{
 
1587
 
 
1588
// the list of the directories to search for message catalog files
 
1589
wxArrayString gs_searchPrefixes;
 
1590
 
 
1591
// return the directories to search for message catalogs under the given
 
1592
// prefix, separated by wxPATH_SEP
 
1593
wxString GetMsgCatalogSubdirs(const wxString& prefix, const wxString& lang)
 
1594
{
 
1595
    // Search first in Unix-standard prefix/lang/LC_MESSAGES, then in
 
1596
    // prefix/lang and finally in just prefix.
 
1597
    //
 
1598
    // Note that we use LC_MESSAGES on all platforms and not just Unix, because
 
1599
    // it doesn't cost much to look into one more directory and doing it this
 
1600
    // way has two important benefits:
 
1601
    // a) we don't break compatibility with wx-2.6 and older by stopping to
 
1602
    //    look in a directory where the catalogs used to be and thus silently
 
1603
    //    breaking apps after they are recompiled against the latest wx
 
1604
    // b) it makes it possible to package app's support files in the same
 
1605
    //    way on all target platforms
 
1606
    const wxString pathPrefix = wxFileName(prefix, lang).GetFullPath();
 
1607
 
 
1608
    wxString searchPath;
 
1609
    searchPath.reserve(4*pathPrefix.length());
 
1610
    searchPath << pathPrefix << wxFILE_SEP_PATH << "LC_MESSAGES" << wxPATH_SEP
 
1611
            << prefix << wxFILE_SEP_PATH << wxPATH_SEP
 
1612
            << pathPrefix;
 
1613
 
 
1614
    return searchPath;
 
1615
}
 
1616
 
 
1617
bool HasMsgCatalogInDir(const wxString& dir, const wxString& domain)
 
1618
{
 
1619
    return wxFileName(dir, domain, "mo").FileExists() ||
 
1620
           wxFileName(dir + wxFILE_SEP_PATH + "LC_MESSAGES", domain, "mo").FileExists();
 
1621
}
 
1622
 
 
1623
// get prefixes to locale directories; if lang is empty, don't point to
 
1624
// OSX's .lproj bundles
 
1625
wxArrayString GetSearchPrefixes(const wxString& lang = wxString())
 
1626
{
 
1627
    wxArrayString paths;
 
1628
 
 
1629
    // first take the entries explicitly added by the program
 
1630
    paths = gs_searchPrefixes;
 
1631
 
 
1632
#if wxUSE_STDPATHS
 
1633
    // then look in the standard location
 
1634
    wxString stdp;
 
1635
    if ( lang.empty() )
 
1636
    {
 
1637
        stdp = wxStandardPaths::Get().GetResourcesDir();
 
1638
    }
 
1639
    else
 
1640
    {
 
1641
        stdp = wxStandardPaths::Get().
 
1642
            GetLocalizedResourcesDir(lang, wxStandardPaths::ResourceCat_Messages);
 
1643
    }
 
1644
    if ( paths.Index(stdp) == wxNOT_FOUND )
 
1645
        paths.Add(stdp);
 
1646
#endif // wxUSE_STDPATHS
 
1647
 
 
1648
    // last look in default locations
 
1649
#ifdef __UNIX__
 
1650
    // LC_PATH is a standard env var containing the search path for the .mo
 
1651
    // files
 
1652
    const char *pszLcPath = wxGetenv("LC_PATH");
 
1653
    if ( pszLcPath )
 
1654
    {
 
1655
        const wxString lcp = pszLcPath;
 
1656
        if ( paths.Index(lcp) == wxNOT_FOUND )
 
1657
            paths.Add(lcp);
 
1658
    }
 
1659
 
 
1660
    // also add the one from where wxWin was installed:
 
1661
    wxString wxp = wxGetInstallPrefix();
 
1662
    if ( !wxp.empty() )
 
1663
    {
 
1664
        wxp += wxS("/share/locale");
 
1665
        if ( paths.Index(wxp) == wxNOT_FOUND )
 
1666
            paths.Add(wxp);
 
1667
    }
 
1668
#endif // __UNIX__
 
1669
 
 
1670
    return paths;
 
1671
}
 
1672
 
 
1673
// construct the search path for the given language
 
1674
wxString GetFullSearchPath(const wxString& lang)
 
1675
{
 
1676
    wxString searchPath;
 
1677
    searchPath.reserve(500);
 
1678
 
 
1679
    const wxArrayString prefixes = GetSearchPrefixes(lang);
 
1680
 
 
1681
    for ( wxArrayString::const_iterator i = prefixes.begin();
 
1682
          i != prefixes.end();
 
1683
          ++i )
 
1684
    {
 
1685
        const wxString p = GetMsgCatalogSubdirs(*i, lang);
 
1686
 
 
1687
        if ( !searchPath.empty() )
 
1688
            searchPath += wxPATH_SEP;
 
1689
        searchPath += p;
 
1690
    }
 
1691
 
 
1692
    return searchPath;
 
1693
}
 
1694
 
 
1695
} // anonymous namespace
 
1696
 
 
1697
 
 
1698
void wxFileTranslationsLoader::AddCatalogLookupPathPrefix(const wxString& prefix)
 
1699
{
 
1700
    if ( gs_searchPrefixes.Index(prefix) == wxNOT_FOUND )
 
1701
    {
 
1702
        gs_searchPrefixes.Add(prefix);
 
1703
    }
 
1704
    //else: already have it
 
1705
}
 
1706
 
 
1707
 
 
1708
wxMsgCatalog *wxFileTranslationsLoader::LoadCatalog(const wxString& domain,
 
1709
                                                    const wxString& lang)
 
1710
{
 
1711
    wxString searchPath = GetFullSearchPath(lang);
 
1712
 
 
1713
    wxLogTrace(TRACE_I18N, wxS("Looking for \"%s.mo\" in search path \"%s\""),
 
1714
                domain, searchPath);
 
1715
 
 
1716
    wxFileName fn(domain);
 
1717
    fn.SetExt(wxS("mo"));
 
1718
 
 
1719
    wxString strFullName;
 
1720
    if ( !wxFindFileInPath(&strFullName, searchPath, fn.GetFullPath()) )
 
1721
        return NULL;
 
1722
 
 
1723
    // open file and read its data
 
1724
    wxLogVerbose(_("using catalog '%s' from '%s'."), domain, strFullName.c_str());
 
1725
    wxLogTrace(TRACE_I18N, wxS("Using catalog \"%s\"."), strFullName.c_str());
 
1726
 
 
1727
    return wxMsgCatalog::CreateFromFile(strFullName, domain);
 
1728
}
 
1729
 
 
1730
 
 
1731
wxArrayString wxFileTranslationsLoader::GetAvailableTranslations(const wxString& domain) const
 
1732
{
 
1733
    wxArrayString langs;
 
1734
    const wxArrayString prefixes = GetSearchPrefixes();
 
1735
 
 
1736
    wxLogTrace(TRACE_I18N,
 
1737
               "looking for available translations of \"%s\" in search path \"%s\"",
 
1738
               domain, wxJoin(prefixes, wxPATH_SEP[0]));
 
1739
 
 
1740
    for ( wxArrayString::const_iterator i = prefixes.begin();
 
1741
          i != prefixes.end();
 
1742
          ++i )
 
1743
    {
 
1744
        if ( i->empty() )
 
1745
            continue;
 
1746
        wxDir dir;
 
1747
        if ( !dir.Open(*i) )
 
1748
            continue;
 
1749
 
 
1750
        wxString lang;
 
1751
        for ( bool ok = dir.GetFirst(&lang, "", wxDIR_DIRS);
 
1752
              ok;
 
1753
              ok = dir.GetNext(&lang) )
 
1754
        {
 
1755
            const wxString langdir = *i + wxFILE_SEP_PATH + lang;
 
1756
            if ( HasMsgCatalogInDir(langdir, domain) )
 
1757
            {
 
1758
#ifdef __WXOSX__
 
1759
                wxString rest;
 
1760
                if ( lang.EndsWith(".lproj", &rest) )
 
1761
                    lang = rest;
 
1762
#endif // __WXOSX__
 
1763
 
 
1764
                wxLogTrace(TRACE_I18N,
 
1765
                           "found %s translation of \"%s\"", lang, domain);
 
1766
                langs.push_back(lang);
 
1767
            }
 
1768
        }
 
1769
    }
 
1770
 
 
1771
    return langs;
 
1772
}
 
1773
 
 
1774
 
 
1775
// ----------------------------------------------------------------------------
 
1776
// wxResourceTranslationsLoader
 
1777
// ----------------------------------------------------------------------------
 
1778
 
 
1779
#ifdef __WINDOWS__
 
1780
 
 
1781
wxMsgCatalog *wxResourceTranslationsLoader::LoadCatalog(const wxString& domain,
 
1782
                                                        const wxString& lang)
 
1783
{
 
1784
    const void *mo_data = NULL;
 
1785
    size_t mo_size = 0;
 
1786
 
 
1787
    const wxString resname = wxString::Format("%s_%s", domain, lang);
 
1788
 
 
1789
    if ( !wxLoadUserResource(&mo_data, &mo_size,
 
1790
                             resname,
 
1791
                             GetResourceType(),
 
1792
                             GetModule()) )
 
1793
        return NULL;
 
1794
 
 
1795
    wxLogTrace(TRACE_I18N,
 
1796
               "Using catalog from Windows resource \"%s\".", resname);
 
1797
 
 
1798
    wxMsgCatalog *cat = wxMsgCatalog::CreateFromData(
 
1799
        wxCharBuffer::CreateNonOwned(static_cast<const char*>(mo_data), mo_size),
 
1800
        domain);
 
1801
 
 
1802
    if ( !cat )
 
1803
    {
 
1804
        wxLogWarning(_("Resource '%s' is not a valid message catalog."), resname);
 
1805
    }
 
1806
 
 
1807
    return cat;
 
1808
}
 
1809
 
 
1810
namespace
 
1811
{
 
1812
 
 
1813
struct EnumCallbackData
 
1814
{
 
1815
    wxString prefix;
 
1816
    wxArrayString langs;
 
1817
};
 
1818
 
 
1819
BOOL CALLBACK EnumTranslations(HMODULE WXUNUSED(hModule),
 
1820
                               LPCTSTR WXUNUSED(lpszType),
 
1821
                               LPTSTR lpszName,
 
1822
                               LONG_PTR lParam)
 
1823
{
 
1824
    wxString name(lpszName);
 
1825
    name.MakeLower(); // resource names are case insensitive
 
1826
 
 
1827
    EnumCallbackData *data = reinterpret_cast<EnumCallbackData*>(lParam);
 
1828
 
 
1829
    wxString lang;
 
1830
    if ( name.StartsWith(data->prefix, &lang) && !lang.empty() )
 
1831
        data->langs.push_back(lang);
 
1832
 
 
1833
    return TRUE; // continue enumeration
 
1834
}
 
1835
 
 
1836
} // anonymous namespace
 
1837
 
 
1838
 
 
1839
wxArrayString wxResourceTranslationsLoader::GetAvailableTranslations(const wxString& domain) const
 
1840
{
 
1841
    EnumCallbackData data;
 
1842
    data.prefix = domain + "_";
 
1843
    data.prefix.MakeLower(); // resource names are case insensitive
 
1844
 
 
1845
    if ( !EnumResourceNames(GetModule(),
 
1846
                            GetResourceType().t_str(),
 
1847
                            EnumTranslations,
 
1848
                            reinterpret_cast<LONG_PTR>(&data)) )
 
1849
    {
 
1850
        const DWORD err = GetLastError();
 
1851
        if ( err != NO_ERROR && err != ERROR_RESOURCE_TYPE_NOT_FOUND )
 
1852
        {
 
1853
            wxLogSysError(_("Couldn't enumerate translations"));
 
1854
        }
 
1855
    }
 
1856
 
 
1857
    return data.langs;
 
1858
}
 
1859
 
 
1860
#endif // __WINDOWS__
 
1861
 
 
1862
 
 
1863
// ----------------------------------------------------------------------------
 
1864
// wxTranslationsModule module (for destruction of gs_translations)
 
1865
// ----------------------------------------------------------------------------
 
1866
 
 
1867
class wxTranslationsModule: public wxModule
 
1868
{
 
1869
    DECLARE_DYNAMIC_CLASS(wxTranslationsModule)
 
1870
    public:
 
1871
        wxTranslationsModule() {}
 
1872
 
 
1873
        bool OnInit()
 
1874
        {
 
1875
            return true;
 
1876
        }
 
1877
 
 
1878
        void OnExit()
 
1879
        {
 
1880
            if ( gs_translationsOwned )
 
1881
                delete gs_translations;
 
1882
            gs_translations = NULL;
 
1883
            gs_translationsOwned = true;
 
1884
        }
 
1885
};
 
1886
 
 
1887
IMPLEMENT_DYNAMIC_CLASS(wxTranslationsModule, wxModule)
 
1888
 
 
1889
#endif // wxUSE_INTL