X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/5918786f9f81568935710ee9d5ebe1735faeabb5..00e075e5517eb85c3fd4e4134f19d87b91ed4bc0:/src/common/intl.cpp diff --git a/src/common/intl.cpp b/src/common/intl.cpp index 3f592d7881..0638da4e95 100644 --- a/src/common/intl.cpp +++ b/src/common/intl.cpp @@ -1,12 +1,13 @@ ///////////////////////////////////////////////////////////////////////////// // Name: src/common/intl.cpp -// Purpose: Internationalization and localisation for wxWindows +// Purpose: Internationalization and localisation for wxWidgets // Author: Vadim Zeitlin -// Modified by: +// Modified by: Michael N. Filippov +// (2003/09/30 - PluralForms support) // Created: 29/01/98 // RCS-ID: $Id$ // Copyright: (c) 1998 Vadim Zeitlin -// Licence: wxWindows license +// Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// // ============================================================================ @@ -17,10 +18,6 @@ // headers // ---------------------------------------------------------------------------- -#ifdef __GNUG__ - #pragma implementation "intl.h" -#endif - // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" @@ -28,32 +25,35 @@ #pragma hdrstop #endif -#if wxUSE_INTL - -// standard headers -#include -#include -#include -#ifdef HAVE_LANGINFO_H - #include +#ifdef __EMX__ +// The following define is needed by Innotek's libc to +// make the definition of struct localeconv available. +#define __INTERNAL_DEFS #endif -// wxWindows +#if wxUSE_INTL + #ifndef WX_PRECOMP + #include "wx/dynarray.h" #include "wx/string.h" #include "wx/intl.h" #include "wx/log.h" - #include "wx/debug.h" #include "wx/utils.h" - #include "wx/dynarray.h" + #include "wx/app.h" + #include "wx/hashmap.h" + #include "wx/module.h" #endif // WX_PRECOMP -#include "wx/file.h" -#include "wx/tokenzr.h" -#include "wx/module.h" -#include "wx/fontmap.h" -#include "wx/encconv.h" -#include "wx/hashmap.h" +#ifndef __WXWINCE__ + #include +#endif + +// standard headers +#include +#include +#ifdef HAVE_LANGINFO_H + #include +#endif #ifdef __WIN32__ #include "wx/msw/private.h" @@ -61,8 +61,21 @@ #include "wx/fontmap.h" // for CharsetToEncoding() #endif -#if defined(__WXMAC__) - #include "wx/mac/private.h" // includes mac headers +#include "wx/file.h" +#include "wx/filename.h" +#include "wx/tokenzr.h" +#include "wx/fontmap.h" +#include "wx/encconv.h" +#include "wx/scopedptr.h" +#include "wx/apptrait.h" +#include "wx/stdpaths.h" +#include "wx/hashset.h" + +#if defined(__WXOSX__) + #include "wx/osx/core/cfref.h" + #include + #include + #include "wx/osx/core/cfstring.h" #endif // ---------------------------------------------------------------------------- @@ -81,86 +94,778 @@ typedef wxUint32 size_t32; const size_t32 MSGCATALOG_MAGIC = 0x950412de; const size_t32 MSGCATALOG_MAGIC_SW = 0xde120495; -// extension of ".mo" files -#define MSGCATALOG_EXTENSION _T(".mo") - -// the constants describing the format of lang_LANG locale string +// the constants describing the format of ll_CC locale string static const size_t LEN_LANG = 2; static const size_t LEN_SUBLANG = 2; static const size_t LEN_FULL = LEN_LANG + 1 + LEN_SUBLANG; // 1 for '_' +#define TRACE_I18N wxS("i18n") + // ---------------------------------------------------------------------------- // global functions // ---------------------------------------------------------------------------- -#ifdef __WXDEBUG__ +static wxLocale *wxSetLocale(wxLocale *pLocale); + +namespace +{ + +// get just the language part +inline wxString ExtractLang(const wxString& langFull) +{ + return langFull.Left(LEN_LANG); +} + +// helper functions of GetSystemLanguage() +#ifdef __UNIX__ + +// get everything else (including the leading '_') +inline wxString ExtractNotLang(const wxString& langFull) +{ + return langFull.Mid(LEN_LANG); +} + +#endif // __UNIX__ + +} // anonymous namespace + +// ---------------------------------------------------------------------------- +// Plural forms parser +// ---------------------------------------------------------------------------- + +/* + Simplified Grammar + +Expression: + LogicalOrExpression '?' Expression ':' Expression + LogicalOrExpression + +LogicalOrExpression: + LogicalAndExpression "||" LogicalOrExpression // to (a || b) || c + LogicalAndExpression + +LogicalAndExpression: + EqualityExpression "&&" LogicalAndExpression // to (a && b) && c + EqualityExpression + +EqualityExpression: + RelationalExpression "==" RelationalExperession + RelationalExpression "!=" RelationalExperession + RelationalExpression + +RelationalExpression: + MultiplicativeExpression '>' MultiplicativeExpression + MultiplicativeExpression '<' MultiplicativeExpression + MultiplicativeExpression ">=" MultiplicativeExpression + MultiplicativeExpression "<=" MultiplicativeExpression + MultiplicativeExpression + +MultiplicativeExpression: + PmExpression '%' PmExpression + PmExpression + +PmExpression: + N + Number + '(' Expression ')' +*/ + +class wxPluralFormsToken +{ +public: + enum Type + { + T_ERROR, T_EOF, T_NUMBER, T_N, T_PLURAL, T_NPLURALS, T_EQUAL, T_ASSIGN, + T_GREATER, T_GREATER_OR_EQUAL, T_LESS, T_LESS_OR_EQUAL, + T_REMINDER, T_NOT_EQUAL, + T_LOGICAL_AND, T_LOGICAL_OR, T_QUESTION, T_COLON, T_SEMICOLON, + T_LEFT_BRACKET, T_RIGHT_BRACKET + }; + Type type() const { return m_type; } + void setType(Type type) { m_type = type; } + // for T_NUMBER only + typedef int Number; + Number number() const { return m_number; } + void setNumber(Number num) { m_number = num; } +private: + Type m_type; + Number m_number; +}; + + +class wxPluralFormsScanner +{ +public: + wxPluralFormsScanner(const char* s); + const wxPluralFormsToken& token() const { return m_token; } + bool nextToken(); // returns false if error +private: + const char* m_s; + wxPluralFormsToken m_token; +}; + +wxPluralFormsScanner::wxPluralFormsScanner(const char* s) : m_s(s) +{ + nextToken(); +} + +bool wxPluralFormsScanner::nextToken() +{ + wxPluralFormsToken::Type type = wxPluralFormsToken::T_ERROR; + while (isspace((unsigned char) *m_s)) + { + ++m_s; + } + if (*m_s == 0) + { + type = wxPluralFormsToken::T_EOF; + } + else if (isdigit((unsigned char) *m_s)) + { + wxPluralFormsToken::Number number = *m_s++ - '0'; + while (isdigit((unsigned char) *m_s)) + { + number = number * 10 + (*m_s++ - '0'); + } + m_token.setNumber(number); + type = wxPluralFormsToken::T_NUMBER; + } + else if (isalpha((unsigned char) *m_s)) + { + const char* begin = m_s++; + while (isalnum((unsigned char) *m_s)) + { + ++m_s; + } + size_t size = m_s - begin; + if (size == 1 && memcmp(begin, "n", size) == 0) + { + type = wxPluralFormsToken::T_N; + } + else if (size == 6 && memcmp(begin, "plural", size) == 0) + { + type = wxPluralFormsToken::T_PLURAL; + } + else if (size == 8 && memcmp(begin, "nplurals", size) == 0) + { + type = wxPluralFormsToken::T_NPLURALS; + } + } + else if (*m_s == '=') + { + ++m_s; + if (*m_s == '=') + { + ++m_s; + type = wxPluralFormsToken::T_EQUAL; + } + else + { + type = wxPluralFormsToken::T_ASSIGN; + } + } + else if (*m_s == '>') + { + ++m_s; + if (*m_s == '=') + { + ++m_s; + type = wxPluralFormsToken::T_GREATER_OR_EQUAL; + } + else + { + type = wxPluralFormsToken::T_GREATER; + } + } + else if (*m_s == '<') + { + ++m_s; + if (*m_s == '=') + { + ++m_s; + type = wxPluralFormsToken::T_LESS_OR_EQUAL; + } + else + { + type = wxPluralFormsToken::T_LESS; + } + } + else if (*m_s == '%') + { + ++m_s; + type = wxPluralFormsToken::T_REMINDER; + } + else if (*m_s == '!' && m_s[1] == '=') + { + m_s += 2; + type = wxPluralFormsToken::T_NOT_EQUAL; + } + else if (*m_s == '&' && m_s[1] == '&') + { + m_s += 2; + type = wxPluralFormsToken::T_LOGICAL_AND; + } + else if (*m_s == '|' && m_s[1] == '|') + { + m_s += 2; + type = wxPluralFormsToken::T_LOGICAL_OR; + } + else if (*m_s == '?') + { + ++m_s; + type = wxPluralFormsToken::T_QUESTION; + } + else if (*m_s == ':') + { + ++m_s; + type = wxPluralFormsToken::T_COLON; + } else if (*m_s == ';') { + ++m_s; + type = wxPluralFormsToken::T_SEMICOLON; + } + else if (*m_s == '(') + { + ++m_s; + type = wxPluralFormsToken::T_LEFT_BRACKET; + } + else if (*m_s == ')') + { + ++m_s; + type = wxPluralFormsToken::T_RIGHT_BRACKET; + } + m_token.setType(type); + return type != wxPluralFormsToken::T_ERROR; +} + +class wxPluralFormsNode; -// small class to suppress the translation erros until exit from current scope -class NoTransErr +// NB: Can't use wxDEFINE_SCOPED_PTR_TYPE because wxPluralFormsNode is not +// fully defined yet: +class wxPluralFormsNodePtr { public: - NoTransErr() { ms_suppressCount++; } - ~NoTransErr() { ms_suppressCount--; } + wxPluralFormsNodePtr(wxPluralFormsNode *p = NULL) : m_p(p) {} + ~wxPluralFormsNodePtr(); + wxPluralFormsNode& operator*() const { return *m_p; } + wxPluralFormsNode* operator->() const { return m_p; } + wxPluralFormsNode* get() const { return m_p; } + wxPluralFormsNode* release(); + void reset(wxPluralFormsNode *p); - static bool Suppress() { return ms_suppressCount > 0; } +private: + wxPluralFormsNode *m_p; +}; + +class wxPluralFormsNode +{ +public: + wxPluralFormsNode(const wxPluralFormsToken& token) : m_token(token) {} + const wxPluralFormsToken& token() const { return m_token; } + const wxPluralFormsNode* node(size_t i) const + { return m_nodes[i].get(); } + void setNode(size_t i, wxPluralFormsNode* n); + wxPluralFormsNode* releaseNode(size_t i); + wxPluralFormsToken::Number evaluate(wxPluralFormsToken::Number n) const; private: - static size_t ms_suppressCount; + wxPluralFormsToken m_token; + wxPluralFormsNodePtr m_nodes[3]; }; -size_t NoTransErr::ms_suppressCount = 0; +wxPluralFormsNodePtr::~wxPluralFormsNodePtr() +{ + delete m_p; +} +wxPluralFormsNode* wxPluralFormsNodePtr::release() +{ + wxPluralFormsNode *p = m_p; + m_p = NULL; + return p; +} +void wxPluralFormsNodePtr::reset(wxPluralFormsNode *p) +{ + if (p != m_p) + { + delete m_p; + m_p = p; + } +} + + +void wxPluralFormsNode::setNode(size_t i, wxPluralFormsNode* n) +{ + m_nodes[i].reset(n); +} + +wxPluralFormsNode* wxPluralFormsNode::releaseNode(size_t i) +{ + return m_nodes[i].release(); +} + +wxPluralFormsToken::Number +wxPluralFormsNode::evaluate(wxPluralFormsToken::Number n) const +{ + switch (token().type()) + { + // leaf + case wxPluralFormsToken::T_NUMBER: + return token().number(); + case wxPluralFormsToken::T_N: + return n; + // 2 args + case wxPluralFormsToken::T_EQUAL: + return node(0)->evaluate(n) == node(1)->evaluate(n); + case wxPluralFormsToken::T_NOT_EQUAL: + return node(0)->evaluate(n) != node(1)->evaluate(n); + case wxPluralFormsToken::T_GREATER: + return node(0)->evaluate(n) > node(1)->evaluate(n); + case wxPluralFormsToken::T_GREATER_OR_EQUAL: + return node(0)->evaluate(n) >= node(1)->evaluate(n); + case wxPluralFormsToken::T_LESS: + return node(0)->evaluate(n) < node(1)->evaluate(n); + case wxPluralFormsToken::T_LESS_OR_EQUAL: + return node(0)->evaluate(n) <= node(1)->evaluate(n); + case wxPluralFormsToken::T_REMINDER: + { + wxPluralFormsToken::Number number = node(1)->evaluate(n); + if (number != 0) + { + return node(0)->evaluate(n) % number; + } + else + { + return 0; + } + } + case wxPluralFormsToken::T_LOGICAL_AND: + return node(0)->evaluate(n) && node(1)->evaluate(n); + case wxPluralFormsToken::T_LOGICAL_OR: + return node(0)->evaluate(n) || node(1)->evaluate(n); + // 3 args + case wxPluralFormsToken::T_QUESTION: + return node(0)->evaluate(n) + ? node(1)->evaluate(n) + : node(2)->evaluate(n); + default: + return 0; + } +} -#else // !Debug -class NoTransErr +class wxPluralFormsCalculator { public: - NoTransErr() { } - ~NoTransErr() { } + wxPluralFormsCalculator() : m_nplurals(0), m_plural(0) {} + + // input: number, returns msgstr index + int evaluate(int n) const; + + // input: text after "Plural-Forms:" (e.g. "nplurals=2; plural=(n != 1);"), + // if s == 0, creates default handler + // returns 0 if error + static wxPluralFormsCalculator* make(const char* s = 0); + + ~wxPluralFormsCalculator() {} + + void init(wxPluralFormsToken::Number nplurals, wxPluralFormsNode* plural); + +private: + wxPluralFormsToken::Number m_nplurals; + wxPluralFormsNodePtr m_plural; }; -#endif // Debug/!Debug +wxDEFINE_SCOPED_PTR_TYPE(wxPluralFormsCalculator) -static wxLocale *wxSetLocale(wxLocale *pLocale); +void wxPluralFormsCalculator::init(wxPluralFormsToken::Number nplurals, + wxPluralFormsNode* plural) +{ + m_nplurals = nplurals; + m_plural.reset(plural); +} -// helper functions of GetSystemLanguage() -#ifdef __UNIX__ +int wxPluralFormsCalculator::evaluate(int n) const +{ + if (m_plural.get() == 0) + { + return 0; + } + wxPluralFormsToken::Number number = m_plural->evaluate(n); + if (number < 0 || number > m_nplurals) + { + return 0; + } + return number; +} -// get just the language part -static inline wxString ExtractLang(const wxString& langFull) + +class wxPluralFormsParser { - return langFull.Left(LEN_LANG); +public: + wxPluralFormsParser(wxPluralFormsScanner& scanner) : m_scanner(scanner) {} + bool parse(wxPluralFormsCalculator& rCalculator); + +private: + wxPluralFormsNode* parsePlural(); + // stops at T_SEMICOLON, returns 0 if error + wxPluralFormsScanner& m_scanner; + const wxPluralFormsToken& token() const; + bool nextToken(); + + wxPluralFormsNode* expression(); + wxPluralFormsNode* logicalOrExpression(); + wxPluralFormsNode* logicalAndExpression(); + wxPluralFormsNode* equalityExpression(); + wxPluralFormsNode* multiplicativeExpression(); + wxPluralFormsNode* relationalExpression(); + wxPluralFormsNode* pmExpression(); +}; + +bool wxPluralFormsParser::parse(wxPluralFormsCalculator& rCalculator) +{ + if (token().type() != wxPluralFormsToken::T_NPLURALS) + return false; + if (!nextToken()) + return false; + if (token().type() != wxPluralFormsToken::T_ASSIGN) + return false; + if (!nextToken()) + return false; + if (token().type() != wxPluralFormsToken::T_NUMBER) + return false; + wxPluralFormsToken::Number nplurals = token().number(); + if (!nextToken()) + return false; + if (token().type() != wxPluralFormsToken::T_SEMICOLON) + return false; + if (!nextToken()) + return false; + if (token().type() != wxPluralFormsToken::T_PLURAL) + return false; + if (!nextToken()) + return false; + if (token().type() != wxPluralFormsToken::T_ASSIGN) + return false; + if (!nextToken()) + return false; + wxPluralFormsNode* plural = parsePlural(); + if (plural == 0) + return false; + if (token().type() != wxPluralFormsToken::T_SEMICOLON) + return false; + if (!nextToken()) + return false; + if (token().type() != wxPluralFormsToken::T_EOF) + return false; + rCalculator.init(nplurals, plural); + return true; } -// get everything else (including the leading '_') -static inline wxString ExtractNotLang(const wxString& langFull) +wxPluralFormsNode* wxPluralFormsParser::parsePlural() { - return langFull.Mid(LEN_LANG); + wxPluralFormsNode* p = expression(); + if (p == NULL) + { + return NULL; + } + wxPluralFormsNodePtr n(p); + if (token().type() != wxPluralFormsToken::T_SEMICOLON) + { + return NULL; + } + return n.release(); } -#endif // __UNIX__ +const wxPluralFormsToken& wxPluralFormsParser::token() const +{ + return m_scanner.token(); +} + +bool wxPluralFormsParser::nextToken() +{ + if (!m_scanner.nextToken()) + return false; + return true; +} + +wxPluralFormsNode* wxPluralFormsParser::expression() +{ + wxPluralFormsNode* p = logicalOrExpression(); + if (p == NULL) + return NULL; + wxPluralFormsNodePtr n(p); + if (token().type() == wxPluralFormsToken::T_QUESTION) + { + wxPluralFormsNodePtr qn(new wxPluralFormsNode(token())); + if (!nextToken()) + { + return 0; + } + p = expression(); + if (p == 0) + { + return 0; + } + qn->setNode(1, p); + if (token().type() != wxPluralFormsToken::T_COLON) + { + return 0; + } + if (!nextToken()) + { + return 0; + } + p = expression(); + if (p == 0) + { + return 0; + } + qn->setNode(2, p); + qn->setNode(0, n.release()); + return qn.release(); + } + return n.release(); +} + +wxPluralFormsNode*wxPluralFormsParser::logicalOrExpression() +{ + wxPluralFormsNode* p = logicalAndExpression(); + if (p == NULL) + return NULL; + wxPluralFormsNodePtr ln(p); + if (token().type() == wxPluralFormsToken::T_LOGICAL_OR) + { + wxPluralFormsNodePtr un(new wxPluralFormsNode(token())); + if (!nextToken()) + { + return 0; + } + p = logicalOrExpression(); + if (p == 0) + { + return 0; + } + wxPluralFormsNodePtr rn(p); // right + if (rn->token().type() == wxPluralFormsToken::T_LOGICAL_OR) + { + // see logicalAndExpression comment + un->setNode(0, ln.release()); + un->setNode(1, rn->releaseNode(0)); + rn->setNode(0, un.release()); + return rn.release(); + } + + + un->setNode(0, ln.release()); + un->setNode(1, rn.release()); + return un.release(); + } + return ln.release(); +} + +wxPluralFormsNode* wxPluralFormsParser::logicalAndExpression() +{ + wxPluralFormsNode* p = equalityExpression(); + if (p == NULL) + return NULL; + wxPluralFormsNodePtr ln(p); // left + if (token().type() == wxPluralFormsToken::T_LOGICAL_AND) + { + wxPluralFormsNodePtr un(new wxPluralFormsNode(token())); // up + if (!nextToken()) + { + return NULL; + } + p = logicalAndExpression(); + if (p == 0) + { + return NULL; + } + wxPluralFormsNodePtr rn(p); // right + if (rn->token().type() == wxPluralFormsToken::T_LOGICAL_AND) + { +// transform 1 && (2 && 3) -> (1 && 2) && 3 +// u r +// l r -> u 3 +// 2 3 l 2 + un->setNode(0, ln.release()); + un->setNode(1, rn->releaseNode(0)); + rn->setNode(0, un.release()); + return rn.release(); + } + + un->setNode(0, ln.release()); + un->setNode(1, rn.release()); + return un.release(); + } + return ln.release(); +} + +wxPluralFormsNode* wxPluralFormsParser::equalityExpression() +{ + wxPluralFormsNode* p = relationalExpression(); + if (p == NULL) + return NULL; + wxPluralFormsNodePtr n(p); + if (token().type() == wxPluralFormsToken::T_EQUAL + || token().type() == wxPluralFormsToken::T_NOT_EQUAL) + { + wxPluralFormsNodePtr qn(new wxPluralFormsNode(token())); + if (!nextToken()) + { + return NULL; + } + p = relationalExpression(); + if (p == NULL) + { + return NULL; + } + qn->setNode(1, p); + qn->setNode(0, n.release()); + return qn.release(); + } + return n.release(); +} + +wxPluralFormsNode* wxPluralFormsParser::relationalExpression() +{ + wxPluralFormsNode* p = multiplicativeExpression(); + if (p == NULL) + return NULL; + wxPluralFormsNodePtr n(p); + if (token().type() == wxPluralFormsToken::T_GREATER + || token().type() == wxPluralFormsToken::T_LESS + || token().type() == wxPluralFormsToken::T_GREATER_OR_EQUAL + || token().type() == wxPluralFormsToken::T_LESS_OR_EQUAL) + { + wxPluralFormsNodePtr qn(new wxPluralFormsNode(token())); + if (!nextToken()) + { + return NULL; + } + p = multiplicativeExpression(); + if (p == NULL) + { + return NULL; + } + qn->setNode(1, p); + qn->setNode(0, n.release()); + return qn.release(); + } + return n.release(); +} + +wxPluralFormsNode* wxPluralFormsParser::multiplicativeExpression() +{ + wxPluralFormsNode* p = pmExpression(); + if (p == NULL) + return NULL; + wxPluralFormsNodePtr n(p); + if (token().type() == wxPluralFormsToken::T_REMINDER) + { + wxPluralFormsNodePtr qn(new wxPluralFormsNode(token())); + if (!nextToken()) + { + return NULL; + } + p = pmExpression(); + if (p == NULL) + { + return NULL; + } + qn->setNode(1, p); + qn->setNode(0, n.release()); + return qn.release(); + } + return n.release(); +} + +wxPluralFormsNode* wxPluralFormsParser::pmExpression() +{ + wxPluralFormsNodePtr n; + if (token().type() == wxPluralFormsToken::T_N + || token().type() == wxPluralFormsToken::T_NUMBER) + { + n.reset(new wxPluralFormsNode(token())); + if (!nextToken()) + { + return NULL; + } + } + else if (token().type() == wxPluralFormsToken::T_LEFT_BRACKET) { + if (!nextToken()) + { + return NULL; + } + wxPluralFormsNode* p = expression(); + if (p == NULL) + { + return NULL; + } + n.reset(p); + if (token().type() != wxPluralFormsToken::T_RIGHT_BRACKET) + { + return NULL; + } + if (!nextToken()) + { + return NULL; + } + } + else + { + return NULL; + } + return n.release(); +} + +wxPluralFormsCalculator* wxPluralFormsCalculator::make(const char* s) +{ + wxPluralFormsCalculatorPtr calculator(new wxPluralFormsCalculator); + if (s != NULL) + { + wxPluralFormsScanner scanner(s); + wxPluralFormsParser p(scanner); + if (!p.parse(*calculator)) + { + return NULL; + } + } + return calculator.release(); +} + + // ---------------------------------------------------------------------------- // wxMsgCatalogFile corresponds to one disk-file message catalog. // // This is a "low-level" class and is used only by wxMsgCatalog +// NOTE: for the documentation of the binary catalog (.MO) files refer to +// the GNU gettext manual: +// http://www.gnu.org/software/autoconf/manual/gettext/MO-Files.html // ---------------------------------------------------------------------------- -WX_DECLARE_EXPORTED_STRING_HASH_MAP(wxString, wxMessagesHash) +WX_DECLARE_EXPORTED_STRING_HASH_MAP(wxString, wxMessagesHash); class wxMsgCatalogFile { public: // ctor & dtor wxMsgCatalogFile(); - ~wxMsgCatalogFile(); + ~wxMsgCatalogFile(); // load the catalog from disk (szDirPrefix corresponds to language) - bool Load(const wxChar *szDirPrefix, const wxChar *szName); + bool Load(const wxString& szDirPrefix, const wxString& szName, + wxPluralFormsCalculatorPtr& rPluralFormsCalculator); // fills the hash with string-translation pairs - void FillHash(wxMessagesHash& hash, bool convertEncoding) const; + bool FillHash(wxMessagesHash& hash, + const wxString& msgIdCharset, + bool convertEncoding) const; + + // return the charset of the strings in this catalog or empty string if + // none/unknown + wxString GetCharset() const { return m_charset; } private: // this implementation is binary compatible with GNU gettext() version 0.10 @@ -168,40 +873,65 @@ private: // an entry in the string table struct wxMsgTableEntry { - size_t32 nLen; // length of the string - size_t32 ofsString; // pointer to the string + size_t32 nLen; // length of the string + size_t32 ofsString; // pointer to the string }; // header of a .mo file struct wxMsgCatalogHeader { - size_t32 magic, // offset +00: magic id - revision, // +04: revision - numStrings; // +08: number of strings in the file - size_t32 ofsOrigTable, // +0C: start of original string table - ofsTransTable; // +10: start of translated string table - size_t32 nHashSize, // +14: hash table size - ofsHashTable; // +18: offset of hash table start + size_t32 magic, // offset +00: magic id + revision, // +04: revision + numStrings; // +08: number of strings in the file + size_t32 ofsOrigTable, // +0C: start of original string table + ofsTransTable; // +10: start of translated string table + size_t32 nHashSize, // +14: hash table size + ofsHashTable; // +18: offset of hash table start }; - // all data is stored here, NULL if no data loaded - size_t8 *m_pData; + // all data is stored here + wxMemoryBuffer m_data; // data description size_t32 m_numStrings; // number of strings in this domain wxMsgTableEntry *m_pOrigTable, // pointer to original strings *m_pTransTable; // translated - const char *StringAtOfs(wxMsgTableEntry *pTable, size_t32 index) const - { return (const char *)(m_pData + Swap(pTable[index].ofsString)); } + wxString m_charset; // from the message catalog header - wxString GetCharset() const; - // utility functions - // big<->little endian - inline size_t32 Swap(size_t32 ui) const; + // swap the 2 halves of 32 bit integer if needed + size_t32 Swap(size_t32 ui) const + { + return m_bSwapped ? (ui << 24) | ((ui & 0xff00) << 8) | + ((ui >> 8) & 0xff00) | (ui >> 24) + : ui; + } - bool m_bSwapped; // wrong endianness? + // just return the pointer to the start of the data as "char *" to + // facilitate doing pointer arithmetic with it + char *StringData() const + { + return static_cast(m_data.GetData()); + } + + const char *StringAtOfs(wxMsgTableEntry *pTable, size_t32 n) const + { + const wxMsgTableEntry * const ent = pTable + n; + + // this check could fail for a corrupt message catalog + size_t32 ofsString = Swap(ent->ofsString); + if ( ofsString + Swap(ent->nLen) > m_data.GetDataLen()) + { + return NULL; + } + + return StringData() + ofsString; + } + + bool m_bSwapped; // wrong endianness? + + wxDECLARE_NO_COPY_CLASS(wxMsgCatalogFile); }; @@ -215,14 +945,20 @@ private: class wxMsgCatalog { public: +#if !wxUSE_UNICODE + wxMsgCatalog() { m_conv = NULL; } + ~wxMsgCatalog(); +#endif + // load the catalog from disk (szDirPrefix corresponds to language) - bool Load(const wxChar *szDirPrefix, const wxChar *szName, bool bConvertEncoding = FALSE); + bool Load(const wxString& dirPrefix, const wxString& name, + const wxString& msgIdCharset, bool bConvertEncoding = false); // get name of the catalog wxString GetName() const { return m_name; } // get the translated string: returns NULL if not found - const wxChar *GetString(const wxChar *sz) const; + const wxString *GetString(const wxString& sz, size_t n = size_t(-1)) const; // public variable pointing to the next element in a linked list (or NULL) wxMsgCatalog *m_pNext; @@ -230,6 +966,14 @@ public: private: wxMessagesHash m_messages; // all messages in the catalog wxString m_name; // name of the domain + +#if !wxUSE_UNICODE + // the conversion corresponding to this catalog charset if we installed it + // as the global one + wxCSConv *m_conv; +#endif + + wxPluralFormsCalculatorPtr m_pluralFormsCalculator; }; // ---------------------------------------------------------------------------- @@ -237,222 +981,390 @@ private: // ---------------------------------------------------------------------------- // the list of the directories to search for message catalog files -static wxArrayString s_searchPrefixes; +static wxArrayString gs_searchPrefixes; // ============================================================================ // implementation // ============================================================================ // ---------------------------------------------------------------------------- -// wxMsgCatalogFile class +// wxLanguageInfo // ---------------------------------------------------------------------------- -// swap the 2 halves of 32 bit integer if needed -size_t32 wxMsgCatalogFile::Swap(size_t32 ui) const +#ifdef __WXMSW__ + +// helper used by wxLanguageInfo::GetLocaleName() and elsewhere to determine +// whether the locale is Unicode-only (it is if this function returns empty +// string) +static wxString wxGetANSICodePageForLocale(LCID lcid) +{ + wxString cp; + + wxChar buffer[16]; + if ( ::GetLocaleInfo(lcid, LOCALE_IDEFAULTANSICODEPAGE, + buffer, WXSIZEOF(buffer)) > 0 ) + { + if ( buffer[0] != wxT('0') || buffer[1] != wxT('\0') ) + cp = buffer; + //else: this locale doesn't use ANSI code page + } + + return cp; +} + +wxUint32 wxLanguageInfo::GetLCID() const { - return m_bSwapped ? (ui << 24) | ((ui & 0xff00) << 8) | - ((ui >> 8) & 0xff00) | (ui >> 24) - : ui; + return MAKELCID(MAKELANGID(WinLang, WinSublang), SORT_DEFAULT); +} + +wxString wxLanguageInfo::GetLocaleName() const +{ + wxString locale; + + const LCID lcid = GetLCID(); + + wxChar buffer[256]; + buffer[0] = wxT('\0'); + if ( !::GetLocaleInfo(lcid, LOCALE_SENGLANGUAGE, buffer, WXSIZEOF(buffer)) ) + { + wxLogLastError(wxT("GetLocaleInfo(LOCALE_SENGLANGUAGE)")); + return locale; + } + + locale << buffer; + if ( ::GetLocaleInfo(lcid, LOCALE_SENGCOUNTRY, + buffer, WXSIZEOF(buffer)) > 0 ) + { + locale << wxT('_') << buffer; + } + + const wxString cp = wxGetANSICodePageForLocale(lcid); + if ( !cp.empty() ) + { + locale << wxT('.') << cp; + } + + return locale; } +#endif // __WXMSW__ + +// ---------------------------------------------------------------------------- +// wxMsgCatalogFile class +// ---------------------------------------------------------------------------- + wxMsgCatalogFile::wxMsgCatalogFile() { - m_pData = NULL; } wxMsgCatalogFile::~wxMsgCatalogFile() { - wxDELETEA(m_pData); } -// return all directories to search for given prefix -static wxString GetAllMsgCatalogSubdirs(const wxChar *prefix, - const wxChar *lang) +// return the directories to search for message catalogs under the given +// prefix, separated by wxPATH_SEP +static +wxString GetMsgCatalogSubdirs(const wxString& prefix, const wxString& lang) { - wxString searchPath; + // Search first in Unix-standard prefix/lang/LC_MESSAGES, then in + // prefix/lang and finally in just prefix. + // + // Note that we use LC_MESSAGES on all platforms and not just Unix, because + // it doesn't cost much to look into one more directory and doing it this + // way has two important benefits: + // a) we don't break compatibility with wx-2.6 and older by stopping to + // look in a directory where the catalogs used to be and thus silently + // breaking apps after they are recompiled against the latest wx + // b) it makes it possible to package app's support files in the same + // way on all target platforms + const wxString pathPrefix = wxFileName(prefix, lang).GetFullPath(); - // search first in prefix/fr/LC_MESSAGES, then in prefix/fr and finally in - // prefix (assuming the language is 'fr') - searchPath << prefix << wxFILE_SEP_PATH << lang << wxFILE_SEP_PATH - << wxT("LC_MESSAGES") << wxPATH_SEP - << prefix << wxFILE_SEP_PATH << lang << wxPATH_SEP - << prefix << wxPATH_SEP; + wxString searchPath; + searchPath.reserve(4*pathPrefix.length()); + searchPath << pathPrefix << wxFILE_SEP_PATH << "LC_MESSAGES" << wxPATH_SEP + << prefix << wxFILE_SEP_PATH << wxPATH_SEP + << pathPrefix; return searchPath; } // construct the search path for the given language -static wxString GetFullSearchPath(const wxChar *lang) +static wxString GetFullSearchPath(const wxString& lang) { - wxString searchPath; - // first take the entries explicitly added by the program - size_t count = s_searchPrefixes.Count(); - for ( size_t n = 0; n < count; n++ ) + wxArrayString paths; + paths.reserve(gs_searchPrefixes.size() + 1); + size_t n, + count = gs_searchPrefixes.size(); + for ( n = 0; n < count; n++ ) { - searchPath << GetAllMsgCatalogSubdirs(s_searchPrefixes[n], lang) - << wxPATH_SEP; + paths.Add(GetMsgCatalogSubdirs(gs_searchPrefixes[n], lang)); } - // LC_PATH is a standard env var containing the search path for the .mo - // files - const wxChar *pszLcPath = wxGetenv(wxT("LC_PATH")); - if ( pszLcPath != NULL ) - searchPath << GetAllMsgCatalogSubdirs(pszLcPath, lang); - // then take the current directory - // FIXME it should be the directory of the executable -#ifdef __WXMAC__ - wxChar cwd[512] ; - wxGetWorkingDirectory( cwd , sizeof( cwd ) ) ; - searchPath << GetAllMsgCatalogSubdirs(cwd, lang); - // generic search paths could be somewhere in the system folder preferences -#else // !Mac - searchPath << GetAllMsgCatalogSubdirs(wxT("."), lang); +#if wxUSE_STDPATHS + // then look in the standard location + const wxString stdp = wxStandardPaths::Get(). + GetLocalizedResourcesDir(lang, wxStandardPaths::ResourceCat_Messages); + if ( paths.Index(stdp) == wxNOT_FOUND ) + paths.Add(stdp); +#endif // wxUSE_STDPATHS + + // last look in default locations #ifdef __UNIX__ - // and finally add some standard ones - searchPath - << GetAllMsgCatalogSubdirs(wxString(wxGetInstallPrefix()) + wxT("/share/locale"), lang) - << GetAllMsgCatalogSubdirs(wxT("/usr/share/locale"), lang) - << GetAllMsgCatalogSubdirs(wxT("/usr/lib/locale"), lang) - << GetAllMsgCatalogSubdirs(wxT("/usr/local/share/locale"), lang); + // LC_PATH is a standard env var containing the search path for the .mo + // files + const char *pszLcPath = wxGetenv("LC_PATH"); + if ( pszLcPath ) + { + const wxString lcp = GetMsgCatalogSubdirs(pszLcPath, lang); + if ( paths.Index(lcp) == wxNOT_FOUND ) + paths.Add(lcp); + } + + // also add the one from where wxWin was installed: + wxString wxp = wxGetInstallPrefix(); + if ( !wxp.empty() ) + { + wxp = GetMsgCatalogSubdirs(wxp + wxS("/share/locale"), lang); + if ( paths.Index(wxp) == wxNOT_FOUND ) + paths.Add(wxp); + } #endif // __UNIX__ -#endif // platform - return searchPath; + // finally construct the full search path + wxString searchPath; + searchPath.reserve(500); + count = paths.size(); + for ( n = 0; n < count; n++ ) + { + searchPath += paths[n]; + if ( n != count - 1 ) + searchPath += wxPATH_SEP; + } + + return searchPath; +} + +// open disk file and read in it's contents +bool wxMsgCatalogFile::Load(const wxString& szDirPrefix, const wxString& szName, + wxPluralFormsCalculatorPtr& rPluralFormsCalculator) +{ + wxCHECK_MSG( szDirPrefix.length() >= LEN_LANG, false, + "invalid language specification" ); + + wxString searchPath; + +#if wxUSE_FONTMAP + // first look for the catalog for this language and the current locale: + // notice that we don't use the system name for the locale as this would + // force us to install catalogs in different locations depending on the + // system but always use the canonical name + wxFontEncoding encSys = wxLocale::GetSystemEncoding(); + if ( encSys != wxFONTENCODING_SYSTEM ) + { + wxString fullname(szDirPrefix); + fullname << wxS('.') << wxFontMapperBase::GetEncodingName(encSys); + searchPath << GetFullSearchPath(fullname) << wxPATH_SEP; + } +#endif // wxUSE_FONTMAP + + + searchPath += GetFullSearchPath(szDirPrefix); + if ( szDirPrefix.length() > LEN_LANG && szDirPrefix[LEN_LANG] == wxS('_') ) + { + // also add just base locale name: for things like "fr_BE" (Belgium + // French) we should use fall back on plain "fr" if no Belgium-specific + // message catalogs exist + searchPath << wxPATH_SEP + << GetFullSearchPath(ExtractLang(szDirPrefix)); + } + + wxLogTrace(TRACE_I18N, wxS("Looking for \"%s.mo\" in search path \"%s\""), + szName, searchPath); + + wxFileName fn(szName); + fn.SetExt(wxS("mo")); + + wxString strFullName; + if ( !wxFindFileInPath(&strFullName, searchPath, fn.GetFullPath()) ) + { + wxLogVerbose(_("catalog file for domain '%s' not found."), szName); + wxLogTrace(TRACE_I18N, wxS("Catalog \"%s.mo\" not found"), szName); + return false; + } + + // open file and read its data + wxLogVerbose(_("using catalog '%s' from '%s'."), szName, strFullName.c_str()); + wxLogTrace(TRACE_I18N, wxS("Using catalog \"%s\"."), strFullName.c_str()); + + wxFile fileMsg(strFullName); + if ( !fileMsg.IsOpened() ) + return false; + + // get the file size (assume it is less than 4Gb...) + wxFileOffset lenFile = fileMsg.Length(); + if ( lenFile == wxInvalidOffset ) + return false; + + size_t nSize = wx_truncate_cast(size_t, lenFile); + wxASSERT_MSG( nSize == lenFile + size_t(0), wxS("message catalog bigger than 4GB?") ); + + // read the whole file in memory + if ( fileMsg.Read(m_data.GetWriteBuf(nSize), nSize) != lenFile ) + return false; + + m_data.UngetWriteBuf(nSize); + + + // examine header + bool bValid = m_data.GetDataLen() > sizeof(wxMsgCatalogHeader); + + const wxMsgCatalogHeader *pHeader = (wxMsgCatalogHeader *)m_data.GetData(); + if ( bValid ) { + // we'll have to swap all the integers if it's true + m_bSwapped = pHeader->magic == MSGCATALOG_MAGIC_SW; + + // check the magic number + bValid = m_bSwapped || pHeader->magic == MSGCATALOG_MAGIC; + } + + if ( !bValid ) { + // it's either too short or has incorrect magic number + wxLogWarning(_("'%s' is not a valid message catalog."), strFullName.c_str()); + + return false; + } + + // initialize + m_numStrings = Swap(pHeader->numStrings); + m_pOrigTable = (wxMsgTableEntry *)(StringData() + + Swap(pHeader->ofsOrigTable)); + m_pTransTable = (wxMsgTableEntry *)(StringData() + + Swap(pHeader->ofsTransTable)); + + // now parse catalog's header and try to extract catalog charset and + // plural forms formula from it: + + const char* headerData = StringAtOfs(m_pOrigTable, 0); + if ( headerData && headerData[0] == '\0' ) + { + // Extract the charset: + const char * const header = StringAtOfs(m_pTransTable, 0); + const char * + cset = strstr(header, "Content-Type: text/plain; charset="); + if ( cset ) + { + cset += 34; // strlen("Content-Type: text/plain; charset=") + + const char * const csetEnd = strchr(cset, '\n'); + if ( csetEnd ) + { + m_charset = wxString(cset, csetEnd - cset); + if ( m_charset == wxS("CHARSET") ) + { + // "CHARSET" is not valid charset, but lazy translator + m_charset.empty(); + } + } + } + // else: incorrectly filled Content-Type header + + // Extract plural forms: + const char * plurals = strstr(header, "Plural-Forms:"); + if ( plurals ) + { + plurals += 13; // strlen("Plural-Forms:") + const char * const pluralsEnd = strchr(plurals, '\n'); + if ( pluralsEnd ) + { + const size_t pluralsLen = pluralsEnd - plurals; + wxCharBuffer buf(pluralsLen); + strncpy(buf.data(), plurals, pluralsLen); + wxPluralFormsCalculator * const + pCalculator = wxPluralFormsCalculator::make(buf); + if ( pCalculator ) + { + rPluralFormsCalculator.reset(pCalculator); + } + else + { + wxLogVerbose(_("Failed to parse Plural-Forms: '%s'"), + buf.data()); + } + } + } + + if ( !rPluralFormsCalculator.get() ) + rPluralFormsCalculator.reset(wxPluralFormsCalculator::make()); + } + + // everything is fine + return true; } -// open disk file and read in it's contents -bool wxMsgCatalogFile::Load(const wxChar *szDirPrefix, const wxChar *szName0) -{ - /* We need to handle locales like de_AT.iso-8859-1 - For this we first chop off the .CHARSET specifier and ignore it. - FIXME: UNICODE SUPPORT: must use CHARSET specifier! - */ - wxString szName = szName0; - if(szName.Find(wxT('.')) != -1) // contains a dot - szName = szName.Left(szName.Find(wxT('.'))); - - wxString searchPath = GetFullSearchPath(szDirPrefix); - const wxChar *sublocale = wxStrchr(szDirPrefix, wxT('_')); - if ( sublocale ) - { - // also add just base locale name: for things like "fr_BE" (belgium - // french) we should use "fr" if no belgium specific message catalogs - // exist - searchPath << GetFullSearchPath(wxString(szDirPrefix). - Left((size_t)(sublocale - szDirPrefix))) - << wxPATH_SEP; - } - - wxString strFile = szName; - strFile += MSGCATALOG_EXTENSION; - - // don't give translation errors here because the wxstd catalog might - // not yet be loaded (and it's normal) - // - // (we're using an object because we have several return paths) - - NoTransErr noTransErr; - wxLogVerbose(_("looking for catalog '%s' in path '%s'."), - szName.c_str(), searchPath.c_str()); - - wxString strFullName; - if ( !wxFindFileInPath(&strFullName, searchPath, strFile) ) { - wxLogVerbose(_("catalog file for domain '%s' not found."), szName.c_str()); - return FALSE; - } - - // open file - wxLogVerbose(_("using catalog '%s' from '%s'."), - szName.c_str(), strFullName.c_str()); - - wxFile fileMsg(strFullName); - if ( !fileMsg.IsOpened() ) - return FALSE; - - // get the file size - off_t nSize = fileMsg.Length(); - if ( nSize == wxInvalidOffset ) - return FALSE; - - // read the whole file in memory - m_pData = new size_t8[nSize]; - if ( fileMsg.Read(m_pData, nSize) != nSize ) { - wxDELETEA(m_pData); - return FALSE; - } - - // examine header - bool bValid = (size_t)nSize > sizeof(wxMsgCatalogHeader); - - wxMsgCatalogHeader *pHeader = (wxMsgCatalogHeader *)m_pData; - if ( bValid ) { - // we'll have to swap all the integers if it's true - m_bSwapped = pHeader->magic == MSGCATALOG_MAGIC_SW; - - // check the magic number - bValid = m_bSwapped || pHeader->magic == MSGCATALOG_MAGIC; - } - - if ( !bValid ) { - // it's either too short or has incorrect magic number - wxLogWarning(_("'%s' is not a valid message catalog."), strFullName.c_str()); - - wxDELETEA(m_pData); - return FALSE; - } - - // initialize - m_numStrings = Swap(pHeader->numStrings); - m_pOrigTable = (wxMsgTableEntry *)(m_pData + - Swap(pHeader->ofsOrigTable)); - m_pTransTable = (wxMsgTableEntry *)(m_pData + - Swap(pHeader->ofsTransTable)); - - // everything is fine - return TRUE; -} - -void wxMsgCatalogFile::FillHash(wxMessagesHash& hash, bool convertEncoding) const -{ - wxString charset = GetCharset(); +bool wxMsgCatalogFile::FillHash(wxMessagesHash& hash, + const wxString& msgIdCharset, + bool convertEncoding) const +{ +#if wxUSE_UNICODE + // this parameter doesn't make sense, we always must convert encoding in + // Unicode build + convertEncoding = true; +#elif wxUSE_FONTMAP + if ( convertEncoding ) + { + // determine if we need any conversion at all + wxFontEncoding encCat = wxFontMapperBase::GetEncodingFromName(m_charset); + if ( encCat == wxLocale::GetSystemEncoding() ) + { + // no need to convert + convertEncoding = false; + } + } +#endif // wxUSE_UNICODE/wxUSE_FONTMAP #if wxUSE_WCHAR_T - wxCSConv *csConv = NULL; - if ( !!charset ) - csConv = new wxCSConv(charset); - - wxMBConv& inputConv = csConv ? *((wxMBConv*)csConv) : *wxConvCurrent; - - for (size_t i = 0; i < m_numStrings; i++) + // conversion to use to convert catalog strings to the GUI encoding + wxMBConv *inputConv, + *inputConvPtr = NULL; // same as inputConv but safely deleteable + if ( convertEncoding && !m_charset.empty() ) { - wxString key(StringAtOfs(m_pOrigTable, i), inputConv); - - #if wxUSE_UNICODE - hash[key] = wxString(StringAtOfs(m_pTransTable, i), inputConv); - #else - if ( convertEncoding ) - hash[key] = - wxString(inputConv.cMB2WC(StringAtOfs(m_pTransTable, i)), - wxConvLocal); - else - hash[key] = StringAtOfs(m_pTransTable, i); - #endif + inputConvPtr = + inputConv = new wxCSConv(m_charset); + } + else // no need or not possible to convert the encoding + { +#if wxUSE_UNICODE + // we must somehow convert the narrow strings in the message catalog to + // wide strings, so use the default conversion if we have no charset + inputConv = wxConvCurrent; +#else // !wxUSE_UNICODE + inputConv = NULL; +#endif // wxUSE_UNICODE/!wxUSE_UNICODE } - delete csConv; -#else // !wxUSE_WCHAR_T - #if wxUSE_FONTMAP + // conversion to apply to msgid strings before looking them up: we only + // need it if the msgids are neither in 7 bit ASCII nor in the same + // encoding as the catalog + wxCSConv *sourceConv = msgIdCharset.empty() || (msgIdCharset == m_charset) + ? NULL + : new wxCSConv(msgIdCharset); + +#elif wxUSE_FONTMAP + wxASSERT_MSG( msgIdCharset.empty(), + wxS("non-ASCII msgid languages only supported if wxUSE_WCHAR_T=1") ); + + wxEncodingConverter converter; if ( convertEncoding ) { wxFontEncoding targetEnc = wxFONTENCODING_SYSTEM; - wxFontEncoding enc = wxFontMapper::Get()->CharsetToEncoding(charset, FALSE); + wxFontEncoding enc = wxFontMapperBase::Get()->CharsetToEncoding(m_charset, false); if ( enc == wxFONTENCODING_SYSTEM ) { - convertEncoding = FALSE; // unknown encoding + convertEncoding = false; // unknown encoding } else { @@ -462,98 +1374,168 @@ void wxMsgCatalogFile::FillHash(wxMessagesHash& hash, bool convertEncoding) cons wxFontEncodingArray a = wxEncodingConverter::GetPlatformEquivalents(enc); if (a[0] == enc) // no conversion needed, locale uses native encoding - convertEncoding = FALSE; + convertEncoding = false; if (a.GetCount() == 0) // we don't know common equiv. under this platform - convertEncoding = FALSE; + convertEncoding = false; targetEnc = a[0]; } } if ( convertEncoding ) { - wxEncodingConverter converter; converter.Init(enc, targetEnc); - - for (size_t i = 0; i < m_numStrings; i++) - { - wxString key(StringAtOfs(m_pOrigTable, i)); - hash[key] = - converter.Convert(wxString(StringAtOfs(m_pTransTable, i))); - } } } +#endif // wxUSE_WCHAR_T/!wxUSE_WCHAR_T + (void)convertEncoding; // get rid of warnings about unused parameter - if ( !convertEncoding ) - #endif // wxUSE_FONTMAP/!wxUSE_FONTMAP + for (size_t32 i = 0; i < m_numStrings; i++) { - for (size_t i = 0; i < m_numStrings; i++) + const char *data = StringAtOfs(m_pOrigTable, i); + if (!data) + return false; // may happen for invalid MO files + + wxString msgid; +#if wxUSE_UNICODE + msgid = wxString(data, *inputConv); +#else // ASCII + #if wxUSE_WCHAR_T + if ( inputConv && sourceConv ) + msgid = wxString(inputConv->cMB2WC(data), *sourceConv); + else + #endif + msgid = data; +#endif // wxUSE_UNICODE + + data = StringAtOfs(m_pTransTable, i); + if (!data) + return false; // may happen for invalid MO files + + size_t length = Swap(m_pTransTable[i].nLen); + size_t offset = 0; + size_t index = 0; + while (offset < length) { - wxString key(StringAtOfs(m_pOrigTable, i)); - hash[key] = StringAtOfs(m_pTransTable, i); - } - } + const char * const str = data + offset; + + wxString msgstr; +#if wxUSE_UNICODE + msgstr = wxString(str, *inputConv); +#elif wxUSE_WCHAR_T + if ( inputConv ) + msgstr = wxString(inputConv->cMB2WC(str), *wxConvUI); + else + msgstr = str; +#else // !wxUSE_WCHAR_T + #if wxUSE_FONTMAP + if ( bConvertEncoding ) + msgstr = wxString(converter.Convert(str)); + else + #endif + msgstr = str; #endif // wxUSE_WCHAR_T/!wxUSE_WCHAR_T -} -wxString wxMsgCatalogFile::GetCharset() const -{ - // first, find encoding header: - const char *hdr = StringAtOfs(m_pOrigTable, 0); - if ( hdr == NULL || hdr[0] != 0 ) - { - // not supported by this catalog, does not have correct header - return wxEmptyString; - } + if ( !msgstr.empty() ) + { + hash[index == 0 ? msgid : msgid + wxChar(index)] = msgstr; + } - wxString header(StringAtOfs(m_pTransTable, 0)); - wxString charset; - int pos = header.Find(wxT("Content-Type: text/plain; charset=")); - if ( pos == wxNOT_FOUND ) - { - // incorrectly filled Content-Type header - return wxEmptyString; + // skip this string + // IMPORTANT: accesses to the 'data' pointer are valid only for + // the first 'length+1' bytes (GNU specs says that the + // final NUL is not counted in length); using wxStrnlen() + // we make sure we don't access memory beyond the valid range + // (which otherwise may happen for invalid MO files): + offset += wxStrnlen(str, length - offset) + 1; + ++index; + } } - size_t n = pos + 34; /*strlen("Content-Type: text/plain; charset=")*/ - while ( header[n] != wxT('\n') ) - charset << header[n++]; - - if ( charset == wxT("CHARSET") ) - { - // "CHARSET" is not valid charset, but lazy translator - return wxEmptyString; - } +#if wxUSE_WCHAR_T + delete sourceConv; + delete inputConvPtr; +#endif // wxUSE_WCHAR_T - return charset; + return true; } + // ---------------------------------------------------------------------------- // wxMsgCatalog class // ---------------------------------------------------------------------------- -bool wxMsgCatalog::Load(const wxChar *szDirPrefix, const wxChar *szName, - bool bConvertEncoding) +#if !wxUSE_UNICODE +wxMsgCatalog::~wxMsgCatalog() +{ + if ( m_conv ) + { + if ( wxConvUI == m_conv ) + { + // we only change wxConvUI if it points to wxConvLocal so we reset + // it back to it too + wxConvUI = &wxConvLocal; + } + + delete m_conv; + } +} +#endif // !wxUSE_UNICODE + +bool wxMsgCatalog::Load(const wxString& dirPrefix, const wxString& name, + const wxString& msgIdCharset, bool bConvertEncoding) { wxMsgCatalogFile file; - m_name = szName; + m_name = name; - if ( file.Load(szDirPrefix, szName) ) + if ( !file.Load(dirPrefix, name, m_pluralFormsCalculator) ) + return false; + + if ( !file.FillHash(m_messages, msgIdCharset, bConvertEncoding) ) + return false; + +#if !wxUSE_UNICODE + // we should use a conversion compatible with the message catalog encoding + // in the GUI if we don't convert the strings to the current conversion but + // as the encoding is global, only change it once, otherwise we could get + // into trouble if we use several message catalogs with different encodings + // + // this is, of course, a hack but it at least allows the program to use + // message catalogs in any encodings without asking the user to change his + // locale + if ( !bConvertEncoding && + !file.GetCharset().empty() && + wxConvUI == &wxConvLocal ) { - file.FillHash(m_messages, bConvertEncoding); - return TRUE; + wxConvUI = + m_conv = new wxCSConv(file.GetCharset()); } +#endif // !wxUSE_UNICODE - return FALSE; + return true; } -const wxChar *wxMsgCatalog::GetString(const wxChar *sz) const +const wxString *wxMsgCatalog::GetString(const wxString& str, size_t n) const { - wxMessagesHash::const_iterator i = m_messages.find(sz); + int index = 0; + if (n != size_t(-1)) + { + index = m_pluralFormsCalculator->evaluate(n); + } + wxMessagesHash::const_iterator i; + if (index != 0) + { + i = m_messages.find(wxString(str) + wxChar(index)); // plural + } + else + { + i = m_messages.find(str); + } + if ( i != m_messages.end() ) { - return i->second.c_str(); + return &i->second; } else return NULL; @@ -565,7 +1547,7 @@ const wxChar *wxMsgCatalog::GetString(const wxChar *sz) const #include "wx/arrimpl.cpp" WX_DECLARE_EXPORTED_OBJARRAY(wxLanguageInfo, wxLanguageInfoArray); -WX_DEFINE_OBJARRAY(wxLanguageInfoArray); +WX_DEFINE_OBJARRAY(wxLanguageInfoArray) wxLanguageInfoArray *wxLocale::ms_languagesDB = NULL; @@ -585,67 +1567,137 @@ wxLanguageInfoArray *wxLocale::ms_languagesDB = NULL; } -wxLocale::wxLocale() +void wxLocale::DoCommonInit() { - m_pszOldLocale = NULL; - m_pMsgCat = NULL; - m_language = wxLANGUAGE_UNKNOWN; + m_pszOldLocale = NULL; + + m_pOldLocale = wxSetLocale(this); + + m_pMsgCat = NULL; + m_language = wxLANGUAGE_UNKNOWN; + m_initialized = false; } // NB: this function has (desired) side effect of changing current locale -bool wxLocale::Init(const wxChar *szName, - const wxChar *szShort, - const wxChar *szLocale, - bool bLoadDefault, - bool bConvertEncoding) -{ - m_strLocale = szName; - m_strShort = szShort; - m_bConvertEncoding = bConvertEncoding; - m_language = wxLANGUAGE_UNKNOWN; - - // change current locale (default: same as long name) - if ( szLocale == NULL ) - { - // the argument to setlocale() - szLocale = szShort; - } - m_pszOldLocale = wxSetlocale(LC_ALL, szLocale); - if ( m_pszOldLocale == NULL ) - wxLogError(_("locale '%s' can not be set."), szLocale); - - // the short name will be used to look for catalog files as well, - // so we need something here - if ( m_strShort.IsEmpty() ) { - // FIXME I don't know how these 2 letter abbreviations are formed, - // this wild guess is surely wrong - if ( szLocale[0] ) - { - m_strShort += (wxChar)wxTolower(szLocale[0]); - if ( szLocale[1] ) - m_strShort += (wxChar)wxTolower(szLocale[1]); - } - } - - // save the old locale to be able to restore it later - m_pOldLocale = wxSetLocale(this); - - // load the default catalog with wxWindows standard messages - m_pMsgCat = NULL; - bool bOk = TRUE; - if ( bLoadDefault ) - bOk = AddCatalog(wxT("wxstd")); - - return bOk; +bool wxLocale::Init(const wxString& name, + const wxString& shortName, + const wxString& locale, + bool bLoadDefault, + bool bConvertEncoding) +{ + wxASSERT_MSG( !m_initialized, + wxS("you can't call wxLocale::Init more than once") ); + + m_initialized = true; + m_strLocale = name; + m_strShort = shortName; + m_bConvertEncoding = bConvertEncoding; + m_language = wxLANGUAGE_UNKNOWN; + + // change current locale (default: same as long name) + wxString szLocale(locale); + if ( szLocale.empty() ) + { + // the argument to setlocale() + szLocale = shortName; + + wxCHECK_MSG( !szLocale.empty(), false, + wxS("no locale to set in wxLocale::Init()") ); + } + + const char *oldLocale = wxSetlocale(LC_ALL, szLocale); + if ( oldLocale ) + m_pszOldLocale = wxStrdup(oldLocale); + else + m_pszOldLocale = NULL; + + if ( m_pszOldLocale == NULL ) + { + wxLogError(_("locale '%s' can not be set."), szLocale); + } + + // the short name will be used to look for catalog files as well, + // so we need something here + if ( m_strShort.empty() ) { + // FIXME I don't know how these 2 letter abbreviations are formed, + // this wild guess is surely wrong + if ( !szLocale.empty() ) + { + m_strShort += (wxChar)wxTolower(szLocale[0]); + if ( szLocale.length() > 1 ) + m_strShort += (wxChar)wxTolower(szLocale[1]); + } + } + + // load the default catalog with wxWidgets standard messages + m_pMsgCat = NULL; + bool bOk = true; + if ( bLoadDefault ) + { + bOk = AddCatalog(wxS("wxstd")); + + // there may be a catalog with toolkit specific overrides, it is not + // an error if this does not exist + if ( bOk ) + { + wxString port(wxPlatformInfo::Get().GetPortIdName()); + if ( !port.empty() ) + { + AddCatalog(port.BeforeFirst(wxS('/')).MakeLower()); + } + } + } + + return bOk; } -bool wxLocale::Init(int language, int flags) + +#if defined(__UNIX__) && wxUSE_UNICODE && !defined(__WXMAC__) +static const char *wxSetlocaleTryUTF8(int c, const wxString& lc) { - wxLanguageInfo *info = NULL; - int lang = language; + const char *l = NULL; - CreateLanguagesDB(); + // NB: We prefer to set UTF-8 locale if it's possible and only fall back to + // non-UTF-8 locale if it fails + + if ( !lc.empty() ) + { + wxString buf(lc); + wxString buf2; + buf2 = buf + wxS(".UTF-8"); + l = wxSetlocale(c, buf2); + if ( !l ) + { + buf2 = buf + wxS(".utf-8"); + l = wxSetlocale(c, buf2); + } + if ( !l ) + { + buf2 = buf + wxS(".UTF8"); + l = wxSetlocale(c, buf2); + } + if ( !l ) + { + buf2 = buf + wxS(".utf8"); + l = wxSetlocale(c, buf2); + } + } + + // if we can't set UTF-8 locale, try non-UTF-8 one: + if ( !l ) + l = wxSetlocale(c, lc); + + return l; +} +#else +#define wxSetlocaleTryUTF8(c, lc) wxSetlocale(c, lc) +#endif + +bool wxLocale::Init(int language, int flags) +{ + bool ret = true; + int lang = language; if (lang == wxLANGUAGE_DEFAULT) { // auto detect the language @@ -655,133 +1707,202 @@ bool wxLocale::Init(int language, int flags) // We failed to detect system language, so we will use English: if (lang == wxLANGUAGE_UNKNOWN) { - return FALSE; + return false; } - if (lang != wxLANGUAGE_DEFAULT) - { - for (size_t i = 0; i < ms_languagesDB->GetCount(); i++) - { - if (ms_languagesDB->Item(i).Language == lang) - { - info = &ms_languagesDB->Item(i); - break; - } - } - } + const wxLanguageInfo *info = GetLanguageInfo(lang); // Unknown language: if (info == NULL) { - wxLogError(wxT("Unknown language %i."), lang); - return FALSE; + wxLogError(wxS("Unknown language %i."), lang); + return false; } wxString name = info->Description; wxString canonical = info->CanonicalName; wxString locale; - const wxChar *retloc; // Set the locale: -#if defined(__UNIX__) && !defined(__WXMAC__) - if (language == wxLANGUAGE_DEFAULT) - locale = wxEmptyString; - else +#if defined(__OS2__) + const char *retloc = wxSetlocale(LC_ALL , wxEmptyString); +#elif defined(__UNIX__) && !defined(__WXMAC__) + if (language != wxLANGUAGE_DEFAULT) locale = info->CanonicalName; - retloc = wxSetlocale(LC_ALL, locale); + const char *retloc = wxSetlocaleTryUTF8(LC_ALL, locale); - if (retloc == NULL) + const wxString langOnly = ExtractLang(locale); + if ( !retloc ) { // Some C libraries don't like xx_YY form and require xx only - retloc = wxSetlocale(LC_ALL, locale.Mid(0,2)); - } - if (retloc == NULL) - { - // Some C libraries (namely glibc) still use old ISO 639, - // so will translate the abbrev for them - wxString mid = locale.Mid(0,2); - if (mid == wxT("he")) locale = wxT("iw") + locale.Mid(3); - else if (mid == wxT("id")) locale = wxT("in") + locale.Mid(3); - else if (mid == wxT("yi")) locale = wxT("ji") + locale.Mid(3); - retloc = wxSetlocale(LC_ALL, locale); + retloc = wxSetlocaleTryUTF8(LC_ALL, langOnly); } - if (retloc == NULL) + +#if wxUSE_FONTMAP + // some systems (e.g. FreeBSD and HP-UX) don't have xx_YY aliases but + // require the full xx_YY.encoding form, so try using UTF-8 because this is + // the only thing we can do generically + // + // TODO: add encodings applicable to each language to the lang DB and try + // them all in turn here + if ( !retloc ) { - // (This time, we changed locale in previous if-branch, so try again.) - // Some C libraries don't like xx_YY form and require xx only - retloc = wxSetlocale(LC_ALL, locale.Mid(0,2)); + const wxChar **names = + wxFontMapperBase::GetAllEncodingNames(wxFONTENCODING_UTF8); + while ( *names ) + { + retloc = wxSetlocale(LC_ALL, locale + wxS('.') + *names++); + if ( retloc ) + break; + } } - if (retloc == NULL) +#endif // wxUSE_FONTMAP + + if ( !retloc ) { - wxLogError(wxT("Cannot set locale to '%s'."), locale.c_str()); - return FALSE; + // Some C libraries (namely glibc) still use old ISO 639, + // so will translate the abbrev for them + wxString localeAlt; + if ( langOnly == wxS("he") ) + localeAlt = wxS("iw") + ExtractNotLang(locale); + else if ( langOnly == wxS("id") ) + localeAlt = wxS("in") + ExtractNotLang(locale); + else if ( langOnly == wxS("yi") ) + localeAlt = wxS("ji") + ExtractNotLang(locale); + else if ( langOnly == wxS("nb") ) + localeAlt = wxS("no_NO"); + else if ( langOnly == wxS("nn") ) + localeAlt = wxS("no_NY"); + + if ( !localeAlt.empty() ) + { + retloc = wxSetlocaleTryUTF8(LC_ALL, localeAlt); + if ( !retloc ) + retloc = wxSetlocaleTryUTF8(LC_ALL, ExtractLang(localeAlt)); + } } + + if ( !retloc ) + ret = false; + +#ifdef __AIX__ + // at least in AIX 5.2 libc is buggy and the string returned from + // setlocale(LC_ALL) can't be passed back to it because it returns 6 + // strings (one for each locale category), i.e. for C locale we get back + // "C C C C C C" + // + // this contradicts IBM own docs but this is not of much help, so just work + // around it in the crudest possible manner + char* p = const_cast(wxStrchr(retloc, ' ')); + if ( p ) + *p = '\0'; +#endif // __AIX__ + #elif defined(__WIN32__) - if (language != wxLANGUAGE_DEFAULT) + const char *retloc = "C"; + if ( language != wxLANGUAGE_DEFAULT ) { - if (info->WinLang == 0) + if ( info->WinLang == 0 ) { - wxLogWarning(wxT("Locale '%s' not supported by OS."), name.c_str()); - retloc = wxT("C"); + wxLogWarning(wxS("Locale '%s' not supported by OS."), name.c_str()); + // retloc already set to "C" } - else + else // language supported by Windows { - wxUint32 lcid = MAKELCID(MAKELANGID(info->WinLang, info->WinSublang), - SORT_DEFAULT); - if (SetThreadLocale(lcid)) - retloc = wxSetlocale(LC_ALL, wxEmptyString); - else + // Windows CE doesn't have SetThreadLocale() and there doesn't seem + // to be any equivalent +#ifndef __WXWINCE__ + const wxUint32 lcid = info->GetLCID(); + + // change locale used by Windows functions + ::SetThreadLocale(lcid); +#endif + + // and also call setlocale() to change locale used by the CRT + locale = info->GetLocaleName(); + if ( locale.empty() ) { - // Windows9X doesn't support SetThreadLocale, so we must - // translate LCID to CRT's setlocale string ourselves - locale.Empty(); - if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) - { - wxChar buffer[256]; - buffer[0] = wxT('\0'); - GetLocaleInfo(lcid, LOCALE_SENGLANGUAGE, buffer, 256); - locale << buffer; - if (GetLocaleInfo(lcid, LOCALE_SENGCOUNTRY, buffer, 256) > 0) - locale << wxT("_") << buffer; - } - if (locale.IsEmpty()) - { - wxLogLastError(wxT("SetThreadLocale")); - wxLogError(wxT("Cannot set locale to language %s."), name.c_str()); - return FALSE; - } - else - retloc = wxSetlocale(LC_ALL, locale); + ret = false; + } + else // have a valid locale + { + retloc = wxSetlocale(LC_ALL, locale); } } } - else + else // language == wxLANGUAGE_DEFAULT + { retloc = wxSetlocale(LC_ALL, wxEmptyString); + } - if (retloc == NULL) +#if wxUSE_UNICODE && (defined(__VISUALC__) || defined(__MINGW32__)) + // VC++ setlocale() (also used by Mingw) can't set locale to languages that + // can only be written using Unicode, therefore wxSetlocale() call fails + // for such languages but we don't want to report it as an error -- so that + // at least message catalogs can be used. + if ( !retloc ) { - wxLogError(wxT("Cannot set locale to language %s."), name.c_str()); - return FALSE; + if ( wxGetANSICodePageForLocale(LOCALE_USER_DEFAULT).empty() ) + { + // we set the locale to a Unicode-only language, don't treat the + // inability of CRT to use it as an error + retloc = "C"; + } } +#endif // CRT not handling Unicode-only languages + + if ( !retloc ) + ret = false; #elif defined(__WXMAC__) - retloc = wxSetlocale(LC_ALL , wxEmptyString); + if (lang == wxLANGUAGE_DEFAULT) + locale = wxEmptyString; + else + locale = info->CanonicalName; + + const char *retloc = wxSetlocale(LC_ALL, locale); + + if ( !retloc ) + { + // Some C libraries don't like xx_YY form and require xx only + retloc = wxSetlocale(LC_ALL, ExtractLang(locale)); + } #else - return FALSE; + wxUnusedVar(flags); + return false; + #define WX_NO_LOCALE_SUPPORT #endif - return Init(name, canonical, wxString(retloc), - (flags & wxLOCALE_LOAD_DEFAULT) != 0, - (flags & wxLOCALE_CONV_ENCODING) != 0); +#ifndef WX_NO_LOCALE_SUPPORT + if ( !ret ) + { + wxLogWarning(_("Cannot set locale to language \"%s\"."), name.c_str()); + + // continue nevertheless and try to load at least the translations for + // this language + } + + if ( !Init(name, canonical, retloc, + (flags & wxLOCALE_LOAD_DEFAULT) != 0, + (flags & wxLOCALE_CONV_ENCODING) != 0) ) + { + ret = false; + } + + if (IsOk()) // setlocale() succeeded + m_language = lang; + + return ret; +#endif // !WX_NO_LOCALE_SUPPORT } void wxLocale::AddCatalogLookupPathPrefix(const wxString& prefix) { - if ( s_searchPrefixes.Index(prefix) == wxNOT_FOUND ) + if ( gs_searchPrefixes.Index(prefix) == wxNOT_FOUND ) { - s_searchPrefixes.Add(prefix); + gs_searchPrefixes.Add(prefix); } //else: already have it } @@ -792,24 +1913,36 @@ void wxLocale::AddCatalogLookupPathPrefix(const wxString& prefix) // init i to avoid compiler warning size_t i = 0, - count = ms_languagesDB->GetCount(); + count = ms_languagesDB->GetCount(); -#if defined(__UNIX__) && !defined(__WXMAC__) +#if defined(__UNIX__) // first get the string identifying the language from the environment wxString langFull; - if (!wxGetEnv(wxT("LC_ALL"), &langFull) && - !wxGetEnv(wxT("LC_MESSAGES"), &langFull) && - !wxGetEnv(wxT("LANG"), &langFull)) +#ifdef __WXMAC__ + wxCFRef userLocaleRef(CFLocaleCopyCurrent()); + + // because the locale identifier (kCFLocaleIdentifier) is formatted a little bit differently, eg + // az_Cyrl_AZ@calendar=buddhist;currency=JPY we just recreate the base info as expected by wx here + + wxCFStringRef str(wxCFRetain((CFStringRef)CFLocaleGetValue(userLocaleRef, kCFLocaleLanguageCode))); + langFull = str.AsString()+"_"; + str.reset(wxCFRetain((CFStringRef)CFLocaleGetValue(userLocaleRef, kCFLocaleCountryCode))); + langFull += str.AsString(); +#else + if (!wxGetEnv(wxS("LC_ALL"), &langFull) && + !wxGetEnv(wxS("LC_MESSAGES"), &langFull) && + !wxGetEnv(wxS("LANG"), &langFull)) { - // no language specified, threat it as English - return wxLANGUAGE_ENGLISH; + // no language specified, treat it as English + return wxLANGUAGE_ENGLISH_US; } - if ( langFull == _T("C") || langFull == _T("POSIX") ) + if ( langFull == wxS("C") || langFull == wxS("POSIX") ) { - // default C locale - return wxLANGUAGE_ENGLISH; + // default C locale is English too + return wxLANGUAGE_ENGLISH_US; } +#endif // the language string has the following form // @@ -831,9 +1964,14 @@ void wxLocale::AddCatalogLookupPathPrefix(const wxString& prefix) // for now we don't use the encoding, although we probably should (doing // translations of the msg catalogs on the fly as required) (TODO) // - // we don't use the modifiers neither but we probably should translate - // "euro" into iso885915 - size_t posEndLang = langFull.find_first_of(_T("@.")); + // we need the modified for languages like Valencian: ca_ES@valencia + // though, remember it + wxString modifier; + size_t posModifier = langFull.find_first_of(wxS("@")); + if ( posModifier != wxString::npos ) + modifier = langFull.Mid(posModifier); + + size_t posEndLang = langFull.find_first_of(wxS("@.")); if ( posEndLang != wxString::npos ) { langFull.Truncate(posEndLang); @@ -844,24 +1982,30 @@ void wxLocale::AddCatalogLookupPathPrefix(const wxString& prefix) // check for this // do we have just the language (or sublang too)? - bool justLang = langFull.Len() == LEN_LANG; + bool justLang = langFull.length() == LEN_LANG; if ( justLang || - (langFull.Len() == LEN_FULL && langFull[LEN_LANG] == wxT('_')) ) + (langFull.length() == LEN_FULL && langFull[LEN_LANG] == wxS('_')) ) { // 0. Make sure the lang is according to latest ISO 639 - // (this is neccessary because glibc uses iw and in instead + // (this is necessary because glibc uses iw and in instead // of he and id respectively). // the language itself (second part is the dialect/sublang) wxString langOrig = ExtractLang(langFull); wxString lang; - if ( langOrig == wxT("iw")) - lang = _T("he"); - else if ( langOrig == wxT("in") ) - lang = wxT("id"); - else if ( langOrig == wxT("ji") ) - lang = wxT("yi"); + if ( langOrig == wxS("iw")) + lang = wxS("he"); + else if (langOrig == wxS("in")) + lang = wxS("id"); + else if (langOrig == wxS("ji")) + lang = wxS("yi"); + else if (langOrig == wxS("no_NO")) + lang = wxS("nb_NO"); + else if (langOrig == wxS("no_NY")) + lang = wxS("nn_NO"); + else if (langOrig == wxS("no")) + lang = wxS("nb_NO"); else lang = langOrig; @@ -872,11 +2016,24 @@ void wxLocale::AddCatalogLookupPathPrefix(const wxString& prefix) } // 1. Try to find the language either as is: - for ( i = 0; i < count; i++ ) + // a) With modifier if set + if ( !modifier.empty() ) { - if ( ms_languagesDB->Item(i).CanonicalName == langFull ) + wxString langFullWithModifier = langFull + modifier; + for ( i = 0; i < count; i++ ) { - break; + if ( ms_languagesDB->Item(i).CanonicalName == langFullWithModifier ) + break; + } + } + + // b) Without modifier + if ( modifier.empty() || i == count ) + { + for ( i = 0; i < count; i++ ) + { + if ( ms_languagesDB->Item(i).CanonicalName == langFull ) + break; } } @@ -916,292 +2073,6 @@ void wxLocale::AddCatalogLookupPathPrefix(const wxString& prefix) } } } -#elif defined(__WXMAC__) - const char* lc = NULL ; - long lang = GetScriptVariable( smSystemScript, smScriptLang) ; - switch( GetScriptManagerVariable( smRegionCode ) ) { - case verUS : - lc = "en_US" ; - break ; - case verFrance : - lc = "fr_FR" ; - break ; - case verBritain : - lc = "en_GB" ; - break ; - case verGermany : - lc = "de_DE" ; - break ; - case verItaly : - lc = "it_IT" ; - break ; - case verNetherlands : - lc = "nl_NL" ; - break ; - case verFlemish : - lc = "nl_BE" ; - break ; - case verSweden : - lc = "sv_SE" ; - break ; - case verSpain : - lc = "es_ES" ; - break ; - case verDenmark : - lc = "da_DK" ; - break ; - case verPortugal : - lc = "pt_PT" ; - break ; - case verFrCanada: - lc = "fr_CA" ; - break ; - case verNorway: - lc = "no_NO" ; - break ; - case verIsrael: - lc = "iw_IL" ; - break ; - case verJapan: - lc = "ja_JP" ; - break ; - case verAustralia: - lc = "en_AU" ; - break ; - case verArabic: - lc = "ar" ; - break ; - case verFinland: - lc = "fi_FI" ; - break ; - case verFrSwiss: - lc = "fr_CH" ; - break ; - case verGrSwiss: - lc = "de_CH" ; - break ; - case verGreece: - lc = "el_GR" ; - break ; - case verIceland: - lc = "is_IS" ; - break ; - case verMalta: - lc = "mt_MT" ; - break ; - case verCyprus: - // _CY is not part of wx, so we have to translate according to the system language - if ( lang == langGreek ) { - lc = "el_GR" ; - } - else if ( lang == langTurkish ) { - lc = "tr_TR" ; - } - break ; - case verTurkey: - lc = "tr_TR" ; - break ; - case verYugoCroatian: - lc = "hr_HR" ; - break ; - case verIndiaHindi: - lc = "hi_IN" ; - break ; - case verPakistanUrdu: - lc = "ur_PK" ; - break ; - case verTurkishModified: - lc = "tr_TR" ; - break ; - case verItalianSwiss: - lc = "it_CH" ; - break ; - case verInternational: - lc = "en" ; - break ; - case verRomania: - lc = "ro_RO" ; - break ; - case verGreecePoly: - lc = "el_GR" ; - break ; - case verLithuania: - lc = "lt_LT" ; - break ; - case verPoland: - lc = "pl_PL" ; - break ; - case verMagyar : - case verHungary: - lc = "hu_HU" ; - break ; - case verEstonia: - lc = "et_EE" ; - break ; - case verLatvia: - lc = "lv_LV" ; - break ; - case verSami: - // not known - break ; - case verFaroeIsl: - lc = "fo_FO" ; - break ; - case verIran: - lc = "fa_IR" ; - break ; - case verRussia: - lc = "ru_RU" ; - break ; - case verIreland: - lc = "ga_IE" ; - break ; - case verKorea: - lc = "ko_KR" ; - break ; - case verChina: - lc = "zh_CN" ; - break ; - case verTaiwan: - lc = "zh_TW" ; - break ; - case verThailand: - lc = "th_TH" ; - break ; - case verCzech: - lc = "cs_CZ" ; - break ; - case verSlovak: - lc = "sk_SK" ; - break ; - case verBengali: - lc = "bn" ; - break ; - case verByeloRussian: - lc = "be_BY" ; - break ; - case verUkraine: - lc = "uk_UA" ; - break ; - case verGreeceAlt: - lc = "el_GR" ; - break ; - case verSerbian: - lc = "sr_YU" ; - break ; - case verSlovenian: - lc = "sl_SI" ; - break ; - case verMacedonian: - lc = "mk_MK" ; - break ; - case verCroatia: - lc = "hr_HR" ; - break ; - case verBrazil: - lc = "pt_BR " ; - break ; - case verBulgaria: - lc = "bg_BG" ; - break ; - case verCatalonia: - lc = "ca_ES" ; - break ; - case verScottishGaelic: - lc = "gd" ; - break ; - case verManxGaelic: - lc = "gv" ; - break ; - case verBreton: - lc = "br" ; - break ; - case verNunavut: - lc = "iu_CA" ; - break ; - case verWelsh: - lc = "cy" ; - break ; - case verIrishGaelicScript: - lc = "ga_IE" ; - break ; - case verEngCanada: - lc = "en_CA" ; - break ; - case verBhutan: - lc = "dz_BT" ; - break ; - case verArmenian: - lc = "hy_AM" ; - break ; - case verGeorgian: - lc = "ka_GE" ; - break ; - case verSpLatinAmerica: - lc = "es_AR" ; - break ; - case verTonga: - lc = "to_TO" ; - break ; - case verFrenchUniversal: - lc = "fr_FR" ; - break ; - case verAustria: - lc = "de_AT" ; - break ; - case verGujarati: - lc = "gu_IN" ; - break ; - case verPunjabi: - lc = "pa" ; - break ; - case verIndiaUrdu: - lc = "ur_IN" ; - break ; - case verVietnam: - lc = "vi_VN" ; - break ; - case verFrBelgium: - lc = "fr_BE" ; - break ; - case verUzbek: - lc = "uz_UZ" ; - break ; - case verSingapore: - lc = "zh_SG" ; - break ; - case verNynorsk: - lc = "nn_NO" ; - break ; - case verAfrikaans: - lc = "af_ZA" ; - break ; - case verEsperanto: - lc = "eo" ; - break ; - case verMarathi: - lc = "mr_IN" ; - break ; - case verTibetan: - lc = "bo" ; - break ; - case verNepal: - lc = "ne_NP" ; - break ; - case verGreenland: - lc = "kl_GL" ; - break ; - default : - break ; - } - for ( i = 0; i < count; i++ ) - { - if ( ms_languagesDB->Item(i).CanonicalName == lc ) - { - break; - } - } - #elif defined(__WIN32__) LCID lcid = GetUserDefaultLCID(); if ( lcid != 0 ) @@ -1237,7 +2108,7 @@ void wxLocale::AddCatalogLookupPathPrefix(const wxString& prefix) // this is a bit strange as under Windows we get the encoding name using its // numeric value and under Unix we do it the other way round, but this just -// reflects the way different systems provide he encoding info +// reflects the way different systems provide the encoding info /* static */ wxString wxLocale::GetSystemEncodingName() @@ -1247,7 +2118,10 @@ wxString wxLocale::GetSystemEncodingName() #if defined(__WIN32__) && !defined(__WXMICROWIN__) // FIXME: what is the error return value for GetACP()? UINT codepage = ::GetACP(); - encname.Printf(_T("windows-%u"), codepage); + encname.Printf(wxS("windows-%u"), codepage); +#elif defined(__WXMAC__) + // default is just empty string, this resolves to the default system + // encoding later #elif defined(__UNIX_LIKE__) #if defined(HAVE_LANGINFO_H) && defined(CODESET) @@ -1255,54 +2129,38 @@ wxString wxLocale::GetSystemEncodingName() // to Unix98) char *oldLocale = strdup(setlocale(LC_CTYPE, NULL)); setlocale(LC_CTYPE, ""); - char *alang = nl_langinfo(CODESET); + const char *alang = nl_langinfo(CODESET); setlocale(LC_CTYPE, oldLocale); free(oldLocale); if ( alang ) { - // 7 bit ASCII encoding has several alternative names which we should - // recognize to avoid warnings about unrecognized encoding on each - // program startup - - // nl_langinfo() under Solaris returns 646 by default which stands for - // ISO-646, i.e. 7 bit ASCII - // - // and recent glibc call it ANSI_X3.4-1968... - if ( strcmp(alang, "646") == 0 || - strcmp(alang, "ANSI_X3.4-1968") == 0 ) - { - encname = _T("US-ASCII"); - } - else - { - encname = wxConvLibc.cMB2WX(alang); - } + encname = wxString::FromAscii( alang ); } - else + else // nl_langinfo() failed #endif // HAVE_LANGINFO_H { // if we can't get at the character set directly, try to see if it's in // the environment variables (in most cases this won't work, but I was // out of ideas) - wxChar *lang = wxGetenv(wxT("LC_ALL")); - wxChar *dot = lang ? wxStrchr(lang, wxT('.')) : (wxChar *)NULL; + char *lang = getenv( "LC_ALL"); + char *dot = lang ? strchr(lang, '.') : NULL; if (!dot) { - lang = wxGetenv(wxT("LC_CTYPE")); + lang = getenv( "LC_CTYPE" ); if ( lang ) - dot = wxStrchr(lang, wxT('.')); + dot = strchr(lang, '.' ); } if (!dot) { - lang = wxGetenv(wxT("LANG")); + lang = getenv( "LANG"); if ( lang ) - dot = wxStrchr(lang, wxT('.')); + dot = strchr(lang, '.'); } if ( dot ) { - encname = dot+1; + encname = wxString::FromAscii( dot+1 ); } } #endif // Win32/Unix @@ -1316,12 +2174,17 @@ wxFontEncoding wxLocale::GetSystemEncoding() #if defined(__WIN32__) && !defined(__WXMICROWIN__) UINT codepage = ::GetACP(); - // wxWindows only knows about CP1250-1257, 932, 936, 949, 950 + // wxWidgets only knows about CP1250-1257, 874, 932, 936, 949, 950 if ( codepage >= 1250 && codepage <= 1257 ) { return (wxFontEncoding)(wxFONTENCODING_CP1250 + codepage - 1250); } + if ( codepage == 874 ) + { + return wxFONTENCODING_CP874; + } + if ( codepage == 932 ) { return wxFONTENCODING_CP932; @@ -1341,34 +2204,124 @@ wxFontEncoding wxLocale::GetSystemEncoding() { return wxFONTENCODING_CP950; } +#elif defined(__WXMAC__) + CFStringEncoding encoding = 0 ; + encoding = CFStringGetSystemEncoding() ; + return wxMacGetFontEncFromSystemEnc( encoding ) ; #elif defined(__UNIX_LIKE__) && wxUSE_FONTMAP - wxString encname = GetSystemEncodingName(); + const wxString encname = GetSystemEncodingName(); if ( !encname.empty() ) { - wxFontEncoding enc = wxFontMapper::Get()-> - CharsetToEncoding(encname, FALSE /* not interactive */); + wxFontEncoding enc = wxFontMapperBase::GetEncodingFromName(encname); + + // on some modern Linux systems (RedHat 8) the default system locale + // is UTF8 -- but it isn't supported by wxGTK1 in ANSI build at all so + // don't even try to use it in this case +#if !wxUSE_UNICODE && \ + ((defined(__WXGTK__) && !defined(__WXGTK20__)) || defined(__WXMOTIF__)) + if ( enc == wxFONTENCODING_UTF8 ) + { + // the most similar supported encoding... + enc = wxFONTENCODING_ISO8859_1; + } +#endif // !wxUSE_UNICODE + + // GetEncodingFromName() returns wxFONTENCODING_DEFAULT for C locale + // (a.k.a. US-ASCII) which is arguably a bug but keep it like this for + // backwards compatibility and just take care to not return + // wxFONTENCODING_DEFAULT from here as this surely doesn't make sense + if ( enc == wxFONTENCODING_DEFAULT ) + { + // we don't have wxFONTENCODING_ASCII, so use the closest one + return wxFONTENCODING_ISO8859_1; + } + + if ( enc != wxFONTENCODING_MAX ) + { + return enc; + } + //else: return wxFONTENCODING_SYSTEM below + } +#endif // Win32/Unix + + return wxFONTENCODING_SYSTEM; +} + +/* static */ +void wxLocale::AddLanguage(const wxLanguageInfo& info) +{ + CreateLanguagesDB(); + ms_languagesDB->Add(info); +} + +/* static */ +const wxLanguageInfo *wxLocale::GetLanguageInfo(int lang) +{ + CreateLanguagesDB(); + + // calling GetLanguageInfo(wxLANGUAGE_DEFAULT) is a natural thing to do, so + // make it work + if ( lang == wxLANGUAGE_DEFAULT ) + lang = GetSystemLanguage(); + + const size_t count = ms_languagesDB->GetCount(); + for ( size_t i = 0; i < count; i++ ) + { + if ( ms_languagesDB->Item(i).Language == lang ) + { + // We need to create a temporary here in order to make this work with BCC in final build mode + wxLanguageInfo *ptr = &ms_languagesDB->Item(i); + return ptr; + } + } + + return NULL; +} + +/* static */ +wxString wxLocale::GetLanguageName(int lang) +{ + const wxLanguageInfo *info = GetLanguageInfo(lang); + if ( !info ) + return wxEmptyString; + else + return info->Description; +} + +/* static */ +const wxLanguageInfo *wxLocale::FindLanguageInfo(const wxString& locale) +{ + CreateLanguagesDB(); + + const wxLanguageInfo *infoRet = NULL; + + const size_t count = ms_languagesDB->GetCount(); + for ( size_t i = 0; i < count; i++ ) + { + const wxLanguageInfo *info = &ms_languagesDB->Item(i); + + if ( wxStricmp(locale, info->CanonicalName) == 0 || + wxStricmp(locale, info->Description) == 0 ) + { + // exact match, stop searching + infoRet = info; + break; + } - // this should probably be considered as a bug in CharsetToEncoding(): - // it shouldn't return wxFONTENCODING_DEFAULT at all - but it does it - // for US-ASCII charset - // - // we, OTOH, definitely shouldn't return it as it doesn't make sense at - // all (which encoding is it?) - if ( enc != wxFONTENCODING_DEFAULT ) + if ( wxStricmp(locale, info->CanonicalName.BeforeFirst(wxS('_'))) == 0 ) { - return enc; + // a match -- but maybe we'll find an exact one later, so continue + // looking + // + // OTOH, maybe we had already found a language match and in this + // case don't overwrite it because the entry for the default + // country always appears first in ms_languagesDB + if ( !infoRet ) + infoRet = info; } - //else: return wxFONTENCODING_SYSTEM below } -#endif // Win32/Unix - - return wxFONTENCODING_SYSTEM; -} -/*static*/ void wxLocale::AddLanguage(const wxLanguageInfo& info) -{ - CreateLanguagesDB(); - ms_languagesDB->Add(info); + return infoRet; } wxString wxLocale::GetSysName() const @@ -1387,164 +2340,802 @@ wxLocale::~wxLocale() delete pTmpCat; } - // restore old locale + // restore old locale pointer wxSetLocale(m_pOldLocale); + wxSetlocale(LC_ALL, m_pszOldLocale); + free((wxChar *)m_pszOldLocale); // const_cast } // get the translation of given string in current locale -const wxChar *wxLocale::GetString(const wxChar *szOrigString, - const wxChar *szDomain) const +const wxString& wxLocale::GetString(const wxString& origString, + const wxString& domain) const +{ + return GetString(origString, origString, size_t(-1), domain); +} + +const wxString& wxLocale::GetString(const wxString& origString, + const wxString& origString2, + size_t n, + const wxString& domain) const { - if ( wxIsEmpty(szOrigString) ) - return _T(""); + if ( origString.empty() ) + return GetUntranslatedString(origString); - const wxChar *pszTrans = NULL; + const wxString *trans = NULL; wxMsgCatalog *pMsgCat; - if ( szDomain != NULL ) + if ( !domain.empty() ) { - pMsgCat = FindCatalog(szDomain); + pMsgCat = FindCatalog(domain); // does the catalog exist? if ( pMsgCat != NULL ) - pszTrans = pMsgCat->GetString(szOrigString); + trans = pMsgCat->GetString(origString, n); } else { // search in all domains for ( pMsgCat = m_pMsgCat; pMsgCat != NULL; pMsgCat = pMsgCat->m_pNext ) { - pszTrans = pMsgCat->GetString(szOrigString); - if ( pszTrans != NULL ) // take the first found + trans = pMsgCat->GetString(origString, n); + if ( trans != NULL ) // take the first found break; } } - if ( pszTrans == NULL ) + if ( trans == NULL ) { -#ifdef __WXDEBUG__ - if ( !NoTransErr::Suppress() ) - { - NoTransErr noTransErr; + wxLogTrace(TRACE_I18N, + wxS("string \"%s\"[%ld] not found in %slocale '%s'."), + origString, (long)n, + wxString::Format(wxS("domain '%s' "), domain).c_str(), + m_strLocale.c_str()); + + if (n == size_t(-1)) + return GetUntranslatedString(origString); + else + return GetUntranslatedString(n == 1 ? origString : origString2); + } - if ( szDomain != NULL ) - { - wxLogTrace(_T("i18n"), - _T("string '%s' not found in domain '%s' for locale '%s'."), - szOrigString, szDomain, m_strLocale.c_str()); - } - else - { - wxLogTrace(_T("i18n"), - _T("string '%s' not found in locale '%s'."), - szOrigString, m_strLocale.c_str()); - } - } -#endif // __WXDEBUG__ + return *trans; +} + +WX_DECLARE_HASH_SET(wxString, wxStringHash, wxStringEqual, + wxLocaleUntranslatedStrings); + +/* static */ +const wxString& wxLocale::GetUntranslatedString(const wxString& str) +{ + static wxLocaleUntranslatedStrings s_strings; + + wxLocaleUntranslatedStrings::iterator i = s_strings.find(str); + if ( i == s_strings.end() ) + return *s_strings.insert(str).first; + + return *i; +} + +wxString wxLocale::GetHeaderValue(const wxString& header, + const wxString& domain) const +{ + if ( header.empty() ) + return wxEmptyString; + + const wxString *trans = NULL; + wxMsgCatalog *pMsgCat; + + if ( !domain.empty() ) + { + pMsgCat = FindCatalog(domain); + + // does the catalog exist? + if ( pMsgCat == NULL ) + return wxEmptyString; - return szOrigString; + trans = pMsgCat->GetString(wxEmptyString, (size_t)-1); + } + else + { + // search in all domains + for ( pMsgCat = m_pMsgCat; pMsgCat != NULL; pMsgCat = pMsgCat->m_pNext ) + { + trans = pMsgCat->GetString(wxEmptyString, (size_t)-1); + if ( trans != NULL ) // take the first found + break; + } } - return pszTrans; + if ( !trans || trans->empty() ) + return wxEmptyString; + + size_t found = trans->find(header); + if ( found == wxString::npos ) + return wxEmptyString; + + found += header.length() + 2 /* ': ' */; + + // Every header is separated by \n + + size_t endLine = trans->find(wxS('\n'), found); + size_t len = (endLine == wxString::npos) ? + wxString::npos : (endLine - found); + + return trans->substr(found, len); } + // find catalog by name in a linked list, return NULL if !found -wxMsgCatalog *wxLocale::FindCatalog(const wxChar *szDomain) const +wxMsgCatalog *wxLocale::FindCatalog(const wxString& domain) const { // linear search in the linked list wxMsgCatalog *pMsgCat; for ( pMsgCat = m_pMsgCat; pMsgCat != NULL; pMsgCat = pMsgCat->m_pNext ) { - if ( wxStricmp(pMsgCat->GetName(), szDomain) == 0 ) - return pMsgCat; + if ( pMsgCat->GetName() == domain ) + return pMsgCat; } return NULL; } +// check if the given locale is provided by OS and C run time +/* static */ +bool wxLocale::IsAvailable(int lang) +{ + const wxLanguageInfo *info = wxLocale::GetLanguageInfo(lang); + wxCHECK_MSG( info, false, wxS("invalid language") ); + +#if defined(__WIN32__) + if ( !info->WinLang ) + return false; + + if ( !::IsValidLocale(info->GetLCID(), LCID_INSTALLED) ) + return false; + +#elif defined(__UNIX__) + + // Test if setting the locale works, then set it back. + const char *oldLocale = wxSetlocaleTryUTF8(LC_ALL, info->CanonicalName); + if ( !oldLocale ) + { + // Some C libraries don't like xx_YY form and require xx only + oldLocale = wxSetlocaleTryUTF8(LC_ALL, ExtractLang(info->CanonicalName)); + if ( !oldLocale ) + return false; + } + // restore the original locale + wxSetlocale(LC_ALL, oldLocale); +#endif + + return true; +} + // check if the given catalog is loaded -bool wxLocale::IsLoaded(const wxChar *szDomain) const +bool wxLocale::IsLoaded(const wxString& szDomain) const +{ + return FindCatalog(szDomain) != NULL; +} + +// add a catalog to our linked list +bool wxLocale::AddCatalog(const wxString& szDomain) { - return FindCatalog(szDomain) != NULL; + return AddCatalog(szDomain, wxLANGUAGE_ENGLISH_US, wxEmptyString); } // add a catalog to our linked list -bool wxLocale::AddCatalog(const wxChar *szDomain) +bool wxLocale::AddCatalog(const wxString& szDomain, + wxLanguage msgIdLanguage, + const wxString& msgIdCharset) + { - wxMsgCatalog *pMsgCat = new wxMsgCatalog; + wxCHECK_MSG( !m_strShort.empty(), false, "must initialize catalog first" ); + + + // It is OK to not load catalog if the msgid language and m_language match, + // in which case we can directly display the texts embedded in program's + // source code: + if ( msgIdLanguage == m_language ) + return true; + - if ( pMsgCat->Load(m_strShort, szDomain, m_bConvertEncoding) ) { - // add it to the head of the list so that in GetString it will - // be searched before the catalogs added earlier - pMsgCat->m_pNext = m_pMsgCat; - m_pMsgCat = pMsgCat; + wxMsgCatalog *pMsgCat = new wxMsgCatalog; + + if ( pMsgCat->Load(m_strShort, szDomain, msgIdCharset, m_bConvertEncoding) ) + { + // add it to the head of the list so that in GetString it will + // be searched before the catalogs added earlier + pMsgCat->m_pNext = m_pMsgCat; + m_pMsgCat = pMsgCat; + + return true; + } - return TRUE; - } - else { // don't add it because it couldn't be loaded anyway delete pMsgCat; - return FALSE; - } + + // If there's no exact match, we may still get partial match where the + // (basic) language is same, but the country differs. For example, it's + // permitted to use en_US strings from sources even if m_language is en_GB: + const wxLanguageInfo *msgIdLangInfo = GetLanguageInfo(msgIdLanguage); + if ( msgIdLangInfo && + ExtractLang(msgIdLangInfo->CanonicalName) == ExtractLang(m_strShort) ) + { + return true; + } + + return false; } // ---------------------------------------------------------------------------- // accessors for locale-dependent data // ---------------------------------------------------------------------------- -#if 0 +#if defined(__WXMSW__) || defined(__WXOSX__) + +namespace +{ + +// This function translates from Unicode date formats described at +// +// http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns +// +// to strftime()-like syntax. This translation is not lossless but we try to do +// our best. + +static wxString TranslateFromUnicodeFormat(const wxString& fmt) +{ + wxString fmtWX; + fmtWX.reserve(fmt.length()); + + char chLast = '\0'; + size_t lastCount = 0; + + const char* formatchars = + "dghHmMsSy" +#ifdef __WXMSW__ + "t" +#else + "EawD" +#endif + ; + for ( wxString::const_iterator p = fmt.begin(); /* end handled inside */; ++p ) + { + if ( p != fmt.end() ) + { + if ( *p == chLast ) + { + lastCount++; + continue; + } + + const wxUniChar ch = (*p).GetValue(); + if ( ch.IsAscii() && strchr(formatchars, ch) ) + { + // these characters come in groups, start counting them + chLast = ch; + lastCount = 1; + continue; + } + } + + // interpret any special characters we collected so far + if ( lastCount ) + { + switch ( chLast ) + { + case 'd': + switch ( lastCount ) + { + case 1: // d + case 2: // dd + // these two are the same as we don't distinguish + // between 1 and 2 digits for days + fmtWX += "%d"; + break; +#ifdef __WXMSW__ + case 3: // ddd + fmtWX += "%a"; + break; + + case 4: // dddd + fmtWX += "%A"; + break; +#endif + default: + wxFAIL_MSG( "too many 'd's" ); + } + break; +#ifndef __WXMSW__ + case 'D': + switch ( lastCount ) + { + case 1: // D + case 2: // DD + case 3: // DDD + fmtWX += "%j"; + break; + + default: + wxFAIL_MSG( "wrong number of 'D's" ); + } + break; + case 'w': + switch ( lastCount ) + { + case 1: // w + case 2: // ww + fmtWX += "%W"; + break; + + default: + wxFAIL_MSG( "wrong number of 'w's" ); + } + break; + case 'E': + switch ( lastCount ) + { + case 1: // E + case 2: // EE + case 3: // EEE + fmtWX += "%a"; + break; + case 4: // EEEE + fmtWX += "%A"; + break; + case 5: // EEEEE + fmtWX += "%a"; + break; + + default: + wxFAIL_MSG( "wrong number of 'E's" ); + } + break; +#endif + case 'M': + switch ( lastCount ) + { + case 1: // M + case 2: // MM + // as for 'd' and 'dd' above + fmtWX += "%m"; + break; + + case 3: + fmtWX += "%b"; + break; + + case 4: + fmtWX += "%B"; + break; + + default: + wxFAIL_MSG( "too many 'M's" ); + } + break; + + case 'y': + switch ( lastCount ) + { + case 1: // y + case 2: // yy + fmtWX += "%y"; + break; + + case 4: // yyyy + fmtWX += "%Y"; + break; + + default: + wxFAIL_MSG( "wrong number of 'y's" ); + } + break; + + case 'H': + switch ( lastCount ) + { + case 1: // H + case 2: // HH + fmtWX += "%H"; + break; + + default: + wxFAIL_MSG( "wrong number of 'H's" ); + } + break; + + case 'h': + switch ( lastCount ) + { + case 1: // h + case 2: // hh + fmtWX += "%I"; + break; + + default: + wxFAIL_MSG( "wrong number of 'h's" ); + } + break; + + case 'm': + switch ( lastCount ) + { + case 1: // m + case 2: // mm + fmtWX += "%M"; + break; + + default: + wxFAIL_MSG( "wrong number of 'm's" ); + } + break; + + case 's': + switch ( lastCount ) + { + case 1: // s + case 2: // ss + fmtWX += "%S"; + break; + + default: + wxFAIL_MSG( "wrong number of 's's" ); + } + break; + + case 'g': + // strftime() doesn't have era string, + // ignore this format + wxASSERT_MSG( lastCount <= 2, "too many 'g's" ); + break; +#ifndef __WXMSW__ + case 'a': + fmtWX += "%p"; + break; +#endif #ifdef __WXMSW__ + case 't': + switch ( lastCount ) + { + case 1: // t + case 2: // tt + fmtWX += "%p"; + break; + + default: + wxFAIL_MSG( "too many 't's" ); + } + break; +#endif + default: + wxFAIL_MSG( "unreachable" ); + } + + chLast = '\0'; + lastCount = 0; + } + + if ( p == fmt.end() ) + break; + + // not a special character so must be just a separator, treat as is + if ( *p == wxT('%') ) + { + // this one needs to be escaped + fmtWX += wxT('%'); + } + + fmtWX += *p; + } + + return fmtWX; +} + +} // anonymous namespace + +#endif // __WXMSW__ || __WXOSX__ + +#if defined(__WXMSW__) + +namespace +{ + +LCTYPE GetLCTYPEFormatFromLocalInfo(wxLocaleInfo index) +{ + switch ( index ) + { + case wxLOCALE_SHORT_DATE_FMT: + return LOCALE_SSHORTDATE; + + case wxLOCALE_LONG_DATE_FMT: + return LOCALE_SLONGDATE; + + case wxLOCALE_TIME_FMT: + return LOCALE_STIMEFORMAT; + + default: + wxFAIL_MSG( "no matching LCTYPE" ); + } + + return 0; +} + +} // anonymous namespace /* static */ -wxString wxLocale::GetInfo(wxLocaleInfo index) +wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory WXUNUSED(cat)) { + wxUint32 lcid = LOCALE_USER_DEFAULT; + if ( wxGetLocale() ) + { + const wxLanguageInfo * const + info = GetLanguageInfo(wxGetLocale()->GetLanguage()); + if ( info ) + lcid = info->GetLCID(); + } + wxString str; - wxChar buffer[256]; - size_t count; - buffer[0] = wxT('\0'); - switch (index) + + wxChar buf[256]; + buf[0] = wxT('\0'); + + switch ( index ) { - case wxSYS_DECIMAL_SEPARATOR: - count = ::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, buffer, 256); - if (!count) - str << "."; - else - str << buffer; + case wxLOCALE_DECIMAL_POINT: + if ( ::GetLocaleInfo(lcid, LOCALE_SDECIMAL, buf, WXSIZEOF(buf)) ) + str = buf; break; - case wxSYS_LIST_SEPARATOR: - count = ::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SLIST, buffer, 256); - if (!count) - str << ","; - else - str << buffer; + + case wxLOCALE_SHORT_DATE_FMT: + case wxLOCALE_LONG_DATE_FMT: + case wxLOCALE_TIME_FMT: + if ( ::GetLocaleInfo(lcid, GetLCTYPEFormatFromLocalInfo(index), + buf, WXSIZEOF(buf)) ) + { + return TranslateFromUnicodeFormat(buf); + } break; - case wxSYS_LEADING_ZERO: // 0 means no leading zero, 1 means leading zero - count = ::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ILZERO, buffer, 256); - if (!count) - str << "0"; - else - str << buffer; + + case wxLOCALE_DATE_TIME_FMT: + // there doesn't seem to be any specific setting for this, so just + // combine date and time ones + // + // we use the short date because this is what "%c" uses by default + // ("%#c" uses long date but we have no way to specify the + // alternate representation here) + { + const wxString datefmt = GetInfo(wxLOCALE_SHORT_DATE_FMT); + if ( datefmt.empty() ) + break; + + const wxString timefmt = GetInfo(wxLOCALE_TIME_FMT); + if ( timefmt.empty() ) + break; + + str << datefmt << ' ' << timefmt; + } break; + default: - wxFAIL_MSG("Unknown System String !"); + wxFAIL_MSG( "unknown wxLocaleInfo" ); } + return str; } -#else // !__WXMSW__ +#elif defined(__WXOSX__) /* static */ -wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory) +wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory WXUNUSED(cat)) +{ + CFLocaleRef userLocaleRefRaw; + if ( wxGetLocale() ) + { + userLocaleRefRaw = CFLocaleCreate + ( + kCFAllocatorDefault, + wxCFStringRef(wxGetLocale()->GetCanonicalName()) + ); + } + else // no current locale, use the default one + { + userLocaleRefRaw = CFLocaleCopyCurrent(); + } + + wxCFRef userLocaleRef(userLocaleRefRaw); + + CFStringRef cfstr = 0; + switch ( index ) + { + case wxLOCALE_THOUSANDS_SEP: + cfstr = (CFStringRef) CFLocaleGetValue(userLocaleRef, kCFLocaleGroupingSeparator); + break; + + case wxLOCALE_DECIMAL_POINT: + cfstr = (CFStringRef) CFLocaleGetValue(userLocaleRef, kCFLocaleDecimalSeparator); + break; + + case wxLOCALE_SHORT_DATE_FMT: + case wxLOCALE_LONG_DATE_FMT: + case wxLOCALE_DATE_TIME_FMT: + case wxLOCALE_TIME_FMT: + { + CFDateFormatterStyle dateStyle = kCFDateFormatterNoStyle; + CFDateFormatterStyle timeStyle = kCFDateFormatterNoStyle; + switch (index ) + { + case wxLOCALE_SHORT_DATE_FMT: + dateStyle = kCFDateFormatterShortStyle; + break; + case wxLOCALE_LONG_DATE_FMT: + dateStyle = kCFDateFormatterFullStyle; + break; + case wxLOCALE_DATE_TIME_FMT: + dateStyle = kCFDateFormatterFullStyle; + timeStyle = kCFDateFormatterMediumStyle; + break; + case wxLOCALE_TIME_FMT: + timeStyle = kCFDateFormatterMediumStyle; + break; + default: + wxFAIL_MSG( "unexpected time locale" ); + return wxString(); + } + wxCFRef dateFormatter( CFDateFormatterCreate + (NULL, userLocaleRef, dateStyle, timeStyle)); + wxCFStringRef cfs = wxCFRetain( CFDateFormatterGetFormat(dateFormatter )); + wxString format = TranslateFromUnicodeFormat(cfs.AsString()); + // we always want full years + format.Replace("%y","%Y"); + return format; + } + break; + + default: + wxFAIL_MSG( "Unknown locale info" ); + return wxString(); + } + + wxCFStringRef str(wxCFRetain(cfstr)); + return str.AsString(); +} + +#else // !__WXMSW__ && !__WXOSX__, assume generic POSIX + +namespace +{ + +wxString GetDateFormatFromLangInfo(wxLocaleInfo index) { - return wxEmptyString; +#ifdef HAVE_LANGINFO_H + // array containing parameters for nl_langinfo() indexes by offset of index + // from wxLOCALE_SHORT_DATE_FMT + static const nl_item items[] = + { + D_FMT, D_T_FMT, D_T_FMT, T_FMT, + }; + + const int nlidx = index - wxLOCALE_SHORT_DATE_FMT; + if ( nlidx < 0 || nlidx >= (int)WXSIZEOF(items) ) + { + wxFAIL_MSG( "logic error in GetInfo() code" ); + return wxString(); + } + + const wxString fmt(nl_langinfo(items[nlidx])); + + // just return the format returned by nl_langinfo() except for long date + // format which we need to recover from date/time format ourselves (but not + // if we failed completely) + if ( fmt.empty() || index != wxLOCALE_LONG_DATE_FMT ) + return fmt; + + // this is not 100% precise but the idea is that a typical date/time format + // under POSIX systems is a combination of a long date format with time one + // so we should be able to get just the long date format by removing all + // time-specific format specifiers + static const char *timeFmtSpecs = "HIklMpPrRsSTXzZ"; + static const char *timeSep = " :./-"; + + wxString fmtDateOnly; + const wxString::const_iterator end = fmt.end(); + wxString::const_iterator lastSep = end; + for ( wxString::const_iterator p = fmt.begin(); p != end; ++p ) + { + if ( strchr(timeSep, *p) ) + { + if ( lastSep == end ) + lastSep = p; + + // skip it for now, we'll discard it if it's followed by a time + // specifier later or add it to fmtDateOnly if it is not + continue; + } + + if ( *p == '%' && + (p + 1 != end) && strchr(timeFmtSpecs, p[1]) ) + { + // time specified found: skip it and any preceding separators + ++p; + lastSep = end; + continue; + } + + if ( lastSep != end ) + { + fmtDateOnly += wxString(lastSep, p); + lastSep = end; + } + + fmtDateOnly += *p; + } + + return fmtDateOnly; +#else // !HAVE_LANGINFO_H + wxUnusedVar(index); + + // no fallback, let the application deal with unavailability of + // nl_langinfo() itself as there is no good way for us to do it (well, we + // could try to reverse engineer the format from strftime() output but this + // looks like too much trouble considering the relatively small number of + // systems without nl_langinfo() still in use) + return wxString(); +#endif // HAVE_LANGINFO_H/!HAVE_LANGINFO_H } -#endif // __WXMSW__/!__WXMSW__ +} // anonymous namespace + +/* static */ +wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory cat) +{ + lconv * const lc = localeconv(); + if ( !lc ) + return wxString(); + + switch ( index ) + { + case wxLOCALE_THOUSANDS_SEP: + if ( cat == wxLOCALE_CAT_NUMBER ) + return lc->thousands_sep; + else if ( cat == wxLOCALE_CAT_MONEY ) + return lc->mon_thousands_sep; + + wxFAIL_MSG( "invalid wxLocaleCategory" ); + break; + + + case wxLOCALE_DECIMAL_POINT: + if ( cat == wxLOCALE_CAT_NUMBER ) + return lc->decimal_point; + else if ( cat == wxLOCALE_CAT_MONEY ) + return lc->mon_decimal_point; + + wxFAIL_MSG( "invalid wxLocaleCategory" ); + break; + + case wxLOCALE_SHORT_DATE_FMT: + case wxLOCALE_LONG_DATE_FMT: + case wxLOCALE_DATE_TIME_FMT: + case wxLOCALE_TIME_FMT: + if ( cat != wxLOCALE_CAT_DATE && cat != wxLOCALE_CAT_DEFAULT ) + { + wxFAIL_MSG( "invalid wxLocaleCategory" ); + break; + } + + return GetDateFormatFromLangInfo(index); + + + default: + wxFAIL_MSG( "unknown wxLocaleInfo value" ); + } -#endif // 0 + return wxString(); +} + +#endif // platform // ---------------------------------------------------------------------------- // global functions and variables @@ -1558,14 +3149,14 @@ static wxLocale *g_pLocale = NULL; wxLocale *wxGetLocale() { - return g_pLocale; + return g_pLocale; } wxLocale *wxSetLocale(wxLocale *pLocale) { - wxLocale *pOld = g_pLocale; - g_pLocale = pLocale; - return pOld; + wxLocale *pOld = g_pLocale; + g_pLocale = pLocale; + return pOld; } @@ -1579,7 +3170,7 @@ class wxLocaleModule: public wxModule DECLARE_DYNAMIC_CLASS(wxLocaleModule) public: wxLocaleModule() {} - bool OnInit() { return TRUE; } + bool OnInit() { return true; } void OnExit() { wxLocale::DestroyLanguagesDB(); } }; @@ -1592,7 +3183,6 @@ IMPLEMENT_DYNAMIC_CLASS(wxLocaleModule, wxModule) // ---------------------------------------------------------------------------- - // --- --- --- generated code begins here --- --- --- // This table is generated by misc/languages/genlang.py @@ -1766,6 +3356,9 @@ IMPLEMENT_DYNAMIC_CLASS(wxLocaleModule, wxModule) #ifndef LANG_RUSSIAN #define LANG_RUSSIAN (0) #endif +#ifndef LANG_SAMI +#define LANG_SAMI (0) +#endif #ifndef LANG_SANSKRIT #define LANG_SANSKRIT (0) #endif @@ -2088,10 +3681,11 @@ IMPLEMENT_DYNAMIC_CLASS(wxLocaleModule, wxModule) #endif // __WIN32__ -#define LNG(wxlang, canonical, winlang, winsublang, desc) \ +#define LNG(wxlang, canonical, winlang, winsublang, layout, desc) \ info.Language = wxlang; \ info.CanonicalName = wxT(canonical); \ - info.Description = desc; \ + info.LayoutDirection = layout; \ + info.Description = wxT(desc); \ SETWINLANG(info, winlang, winsublang) \ AddLanguage(info); @@ -2100,239 +3694,243 @@ void wxLocale::InitLanguagesDB() wxLanguageInfo info; wxStringTokenizer tkn; - LNG(wxLANGUAGE_ABKHAZIAN, "ab" , 0 , 0 , "Abkhazian") - LNG(wxLANGUAGE_AFAR, "aa" , 0 , 0 , "Afar") - LNG(wxLANGUAGE_AFRIKAANS, "af_ZA", LANG_AFRIKAANS , SUBLANG_DEFAULT , "Afrikaans") - LNG(wxLANGUAGE_ALBANIAN, "sq_AL", LANG_ALBANIAN , SUBLANG_DEFAULT , "Albanian") - LNG(wxLANGUAGE_AMHARIC, "am" , 0 , 0 , "Amharic") - LNG(wxLANGUAGE_ARABIC, "ar" , LANG_ARABIC , SUBLANG_DEFAULT , "Arabic") - LNG(wxLANGUAGE_ARABIC_ALGERIA, "ar_DZ", LANG_ARABIC , SUBLANG_ARABIC_ALGERIA , "Arabic (Algeria)") - LNG(wxLANGUAGE_ARABIC_BAHRAIN, "ar_BH", LANG_ARABIC , SUBLANG_ARABIC_BAHRAIN , "Arabic (Bahrain)") - LNG(wxLANGUAGE_ARABIC_EGYPT, "ar_EG", LANG_ARABIC , SUBLANG_ARABIC_EGYPT , "Arabic (Egypt)") - LNG(wxLANGUAGE_ARABIC_IRAQ, "ar_IQ", LANG_ARABIC , SUBLANG_ARABIC_IRAQ , "Arabic (Iraq)") - LNG(wxLANGUAGE_ARABIC_JORDAN, "ar_JO", LANG_ARABIC , SUBLANG_ARABIC_JORDAN , "Arabic (Jordan)") - LNG(wxLANGUAGE_ARABIC_KUWAIT, "ar_KW", LANG_ARABIC , SUBLANG_ARABIC_KUWAIT , "Arabic (Kuwait)") - LNG(wxLANGUAGE_ARABIC_LEBANON, "ar_LB", LANG_ARABIC , SUBLANG_ARABIC_LEBANON , "Arabic (Lebanon)") - LNG(wxLANGUAGE_ARABIC_LIBYA, "ar_LY", LANG_ARABIC , SUBLANG_ARABIC_LIBYA , "Arabic (Libya)") - LNG(wxLANGUAGE_ARABIC_MOROCCO, "ar_MA", LANG_ARABIC , SUBLANG_ARABIC_MOROCCO , "Arabic (Morocco)") - LNG(wxLANGUAGE_ARABIC_OMAN, "ar_OM", LANG_ARABIC , SUBLANG_ARABIC_OMAN , "Arabic (Oman)") - LNG(wxLANGUAGE_ARABIC_QATAR, "ar_QA", LANG_ARABIC , SUBLANG_ARABIC_QATAR , "Arabic (Qatar)") - LNG(wxLANGUAGE_ARABIC_SAUDI_ARABIA, "ar_SA", LANG_ARABIC , SUBLANG_ARABIC_SAUDI_ARABIA , "Arabic (Saudi Arabia)") - LNG(wxLANGUAGE_ARABIC_SUDAN, "ar_SD", 0 , 0 , "Arabic (Sudan)") - LNG(wxLANGUAGE_ARABIC_SYRIA, "ar_SY", LANG_ARABIC , SUBLANG_ARABIC_SYRIA , "Arabic (Syria)") - LNG(wxLANGUAGE_ARABIC_TUNISIA, "ar_TN", LANG_ARABIC , SUBLANG_ARABIC_TUNISIA , "Arabic (Tunisia)") - LNG(wxLANGUAGE_ARABIC_UAE, "ar_AE", LANG_ARABIC , SUBLANG_ARABIC_UAE , "Arabic (Uae)") - LNG(wxLANGUAGE_ARABIC_YEMEN, "ar_YE", LANG_ARABIC , SUBLANG_ARABIC_YEMEN , "Arabic (Yemen)") - LNG(wxLANGUAGE_ARMENIAN, "hy" , LANG_ARMENIAN , SUBLANG_DEFAULT , "Armenian") - LNG(wxLANGUAGE_ASSAMESE, "as" , LANG_ASSAMESE , SUBLANG_DEFAULT , "Assamese") - LNG(wxLANGUAGE_AYMARA, "ay" , 0 , 0 , "Aymara") - LNG(wxLANGUAGE_AZERI, "az" , LANG_AZERI , SUBLANG_DEFAULT , "Azeri") - LNG(wxLANGUAGE_AZERI_CYRILLIC, "az" , LANG_AZERI , SUBLANG_AZERI_CYRILLIC , "Azeri (Cyrillic)") - LNG(wxLANGUAGE_AZERI_LATIN, "az" , LANG_AZERI , SUBLANG_AZERI_LATIN , "Azeri (Latin)") - LNG(wxLANGUAGE_BASHKIR, "ba" , 0 , 0 , "Bashkir") - LNG(wxLANGUAGE_BASQUE, "eu_ES", LANG_BASQUE , SUBLANG_DEFAULT , "Basque") - LNG(wxLANGUAGE_BELARUSIAN, "be_BY", LANG_BELARUSIAN, SUBLANG_DEFAULT , "Belarusian") - LNG(wxLANGUAGE_BENGALI, "bn" , LANG_BENGALI , SUBLANG_DEFAULT , "Bengali") - LNG(wxLANGUAGE_BHUTANI, "dz" , 0 , 0 , "Bhutani") - LNG(wxLANGUAGE_BIHARI, "bh" , 0 , 0 , "Bihari") - LNG(wxLANGUAGE_BISLAMA, "bi" , 0 , 0 , "Bislama") - LNG(wxLANGUAGE_BRETON, "br" , 0 , 0 , "Breton") - LNG(wxLANGUAGE_BULGARIAN, "bg_BG", LANG_BULGARIAN , SUBLANG_DEFAULT , "Bulgarian") - LNG(wxLANGUAGE_BURMESE, "my" , 0 , 0 , "Burmese") - LNG(wxLANGUAGE_CAMBODIAN, "km" , 0 , 0 , "Cambodian") - LNG(wxLANGUAGE_CATALAN, "ca_ES", LANG_CATALAN , SUBLANG_DEFAULT , "Catalan") - LNG(wxLANGUAGE_CHINESE, "zh_CN", LANG_CHINESE , SUBLANG_DEFAULT , "Chinese") - LNG(wxLANGUAGE_CHINESE_SIMPLIFIED, "zh_CN", LANG_CHINESE , SUBLANG_CHINESE_SIMPLIFIED , "Chinese (Simplified)") - LNG(wxLANGUAGE_CHINESE_TRADITIONAL, "zh_CN", LANG_CHINESE , SUBLANG_CHINESE_TRADITIONAL , "Chinese (Traditional)") - LNG(wxLANGUAGE_CHINESE_HONGKONG, "zh_HK", LANG_CHINESE , SUBLANG_CHINESE_HONGKONG , "Chinese (Hongkong)") - LNG(wxLANGUAGE_CHINESE_MACAU, "zh_MO", LANG_CHINESE , SUBLANG_CHINESE_MACAU , "Chinese (Macau)") - LNG(wxLANGUAGE_CHINESE_SINGAPORE, "zh_SG", LANG_CHINESE , SUBLANG_CHINESE_SINGAPORE , "Chinese (Singapore)") - LNG(wxLANGUAGE_CHINESE_TAIWAN, "zh_TW", 0 , 0 , "Chinese (Taiwan)") - LNG(wxLANGUAGE_CORSICAN, "co" , 0 , 0 , "Corsican") - LNG(wxLANGUAGE_CROATIAN, "hr_HR", LANG_CROATIAN , SUBLANG_DEFAULT , "Croatian") - LNG(wxLANGUAGE_CZECH, "cs_CZ", LANG_CZECH , SUBLANG_DEFAULT , "Czech") - LNG(wxLANGUAGE_DANISH, "da_DK", LANG_DANISH , SUBLANG_DEFAULT , "Danish") - LNG(wxLANGUAGE_DUTCH, "nl_NL", LANG_DUTCH , SUBLANG_DUTCH , "Dutch") - LNG(wxLANGUAGE_DUTCH_BELGIAN, "nl_BE", LANG_DUTCH , SUBLANG_DUTCH_BELGIAN , "Dutch (Belgian)") - LNG(wxLANGUAGE_ENGLISH, "en_GB", LANG_ENGLISH , SUBLANG_ENGLISH_UK , "English") - LNG(wxLANGUAGE_ENGLISH_UK, "en_GB", LANG_ENGLISH , SUBLANG_ENGLISH_UK , "English (U.K.)") - LNG(wxLANGUAGE_ENGLISH_US, "en_US", LANG_ENGLISH , SUBLANG_ENGLISH_US , "English (U.S.)") - LNG(wxLANGUAGE_ENGLISH_AUSTRALIA, "en_AU", LANG_ENGLISH , SUBLANG_ENGLISH_AUS , "English (Australia)") - LNG(wxLANGUAGE_ENGLISH_BELIZE, "en_BZ", LANG_ENGLISH , SUBLANG_ENGLISH_BELIZE , "English (Belize)") - LNG(wxLANGUAGE_ENGLISH_BOTSWANA, "en_BW", 0 , 0 , "English (Botswana)") - LNG(wxLANGUAGE_ENGLISH_CANADA, "en_CA", LANG_ENGLISH , SUBLANG_ENGLISH_CAN , "English (Canada)") - LNG(wxLANGUAGE_ENGLISH_CARIBBEAN, "en_CB", LANG_ENGLISH , SUBLANG_ENGLISH_CARIBBEAN , "English (Caribbean)") - LNG(wxLANGUAGE_ENGLISH_DENMARK, "en_DK", 0 , 0 , "English (Denmark)") - LNG(wxLANGUAGE_ENGLISH_EIRE, "en_IE", LANG_ENGLISH , SUBLANG_ENGLISH_EIRE , "English (Eire)") - LNG(wxLANGUAGE_ENGLISH_JAMAICA, "en_JM", LANG_ENGLISH , SUBLANG_ENGLISH_JAMAICA , "English (Jamaica)") - LNG(wxLANGUAGE_ENGLISH_NEW_ZEALAND, "en_NZ", LANG_ENGLISH , SUBLANG_ENGLISH_NZ , "English (New Zealand)") - LNG(wxLANGUAGE_ENGLISH_PHILIPPINES, "en_PH", LANG_ENGLISH , SUBLANG_ENGLISH_PHILIPPINES , "English (Philippines)") - LNG(wxLANGUAGE_ENGLISH_SOUTH_AFRICA, "en_ZA", LANG_ENGLISH , SUBLANG_ENGLISH_SOUTH_AFRICA , "English (South Africa)") - LNG(wxLANGUAGE_ENGLISH_TRINIDAD, "en_TT", LANG_ENGLISH , SUBLANG_ENGLISH_TRINIDAD , "English (Trinidad)") - LNG(wxLANGUAGE_ENGLISH_ZIMBABWE, "en_ZW", LANG_ENGLISH , SUBLANG_ENGLISH_ZIMBABWE , "English (Zimbabwe)") - LNG(wxLANGUAGE_ESPERANTO, "eo" , 0 , 0 , "Esperanto") - LNG(wxLANGUAGE_ESTONIAN, "et_EE", LANG_ESTONIAN , SUBLANG_DEFAULT , "Estonian") - LNG(wxLANGUAGE_FAEROESE, "fo_FO", LANG_FAEROESE , SUBLANG_DEFAULT , "Faeroese") - LNG(wxLANGUAGE_FARSI, "fa_IR", LANG_FARSI , SUBLANG_DEFAULT , "Farsi") - LNG(wxLANGUAGE_FIJI, "fj" , 0 , 0 , "Fiji") - LNG(wxLANGUAGE_FINNISH, "fi_FI", LANG_FINNISH , SUBLANG_DEFAULT , "Finnish") - LNG(wxLANGUAGE_FRENCH, "fr_FR", LANG_FRENCH , SUBLANG_FRENCH , "French") - LNG(wxLANGUAGE_FRENCH_BELGIAN, "fr_BE", LANG_FRENCH , SUBLANG_FRENCH_BELGIAN , "French (Belgian)") - LNG(wxLANGUAGE_FRENCH_CANADIAN, "fr_CA", LANG_FRENCH , SUBLANG_FRENCH_CANADIAN , "French (Canadian)") - LNG(wxLANGUAGE_FRENCH_LUXEMBOURG, "fr_LU", LANG_FRENCH , SUBLANG_FRENCH_LUXEMBOURG , "French (Luxembourg)") - LNG(wxLANGUAGE_FRENCH_MONACO, "fr_MC", LANG_FRENCH , SUBLANG_FRENCH_MONACO , "French (Monaco)") - LNG(wxLANGUAGE_FRENCH_SWISS, "fr_CH", LANG_FRENCH , SUBLANG_FRENCH_SWISS , "French (Swiss)") - LNG(wxLANGUAGE_FRISIAN, "fy" , 0 , 0 , "Frisian") - LNG(wxLANGUAGE_GALICIAN, "gl_ES", 0 , 0 , "Galician") - LNG(wxLANGUAGE_GEORGIAN, "ka" , LANG_GEORGIAN , SUBLANG_DEFAULT , "Georgian") - LNG(wxLANGUAGE_GERMAN, "de_DE", LANG_GERMAN , SUBLANG_GERMAN , "German") - LNG(wxLANGUAGE_GERMAN_AUSTRIAN, "de_AT", LANG_GERMAN , SUBLANG_GERMAN_AUSTRIAN , "German (Austrian)") - LNG(wxLANGUAGE_GERMAN_BELGIUM, "de_BE", 0 , 0 , "German (Belgium)") - LNG(wxLANGUAGE_GERMAN_LIECHTENSTEIN, "de_LI", LANG_GERMAN , SUBLANG_GERMAN_LIECHTENSTEIN , "German (Liechtenstein)") - LNG(wxLANGUAGE_GERMAN_LUXEMBOURG, "de_LU", LANG_GERMAN , SUBLANG_GERMAN_LUXEMBOURG , "German (Luxembourg)") - LNG(wxLANGUAGE_GERMAN_SWISS, "de_CH", LANG_GERMAN , SUBLANG_GERMAN_SWISS , "German (Swiss)") - LNG(wxLANGUAGE_GREEK, "el_GR", LANG_GREEK , SUBLANG_DEFAULT , "Greek") - LNG(wxLANGUAGE_GREENLANDIC, "kl_GL", 0 , 0 , "Greenlandic") - LNG(wxLANGUAGE_GUARANI, "gn" , 0 , 0 , "Guarani") - LNG(wxLANGUAGE_GUJARATI, "gu" , LANG_GUJARATI , SUBLANG_DEFAULT , "Gujarati") - LNG(wxLANGUAGE_HAUSA, "ha" , 0 , 0 , "Hausa") - LNG(wxLANGUAGE_HEBREW, "he_IL", LANG_HEBREW , SUBLANG_DEFAULT , "Hebrew") - LNG(wxLANGUAGE_HINDI, "hi_IN", LANG_HINDI , SUBLANG_DEFAULT , "Hindi") - LNG(wxLANGUAGE_HUNGARIAN, "hu_HU", LANG_HUNGARIAN , SUBLANG_DEFAULT , "Hungarian") - LNG(wxLANGUAGE_ICELANDIC, "is_IS", LANG_ICELANDIC , SUBLANG_DEFAULT , "Icelandic") - LNG(wxLANGUAGE_INDONESIAN, "id_ID", LANG_INDONESIAN, SUBLANG_DEFAULT , "Indonesian") - LNG(wxLANGUAGE_INTERLINGUA, "ia" , 0 , 0 , "Interlingua") - LNG(wxLANGUAGE_INTERLINGUE, "ie" , 0 , 0 , "Interlingue") - LNG(wxLANGUAGE_INUKTITUT, "iu" , 0 , 0 , "Inuktitut") - LNG(wxLANGUAGE_INUPIAK, "ik" , 0 , 0 , "Inupiak") - LNG(wxLANGUAGE_IRISH, "ga_IE", 0 , 0 , "Irish") - LNG(wxLANGUAGE_ITALIAN, "it_IT", LANG_ITALIAN , SUBLANG_ITALIAN , "Italian") - LNG(wxLANGUAGE_ITALIAN_SWISS, "it_CH", LANG_ITALIAN , SUBLANG_ITALIAN_SWISS , "Italian (Swiss)") - LNG(wxLANGUAGE_JAPANESE, "ja_JP", LANG_JAPANESE , SUBLANG_DEFAULT , "Japanese") - LNG(wxLANGUAGE_JAVANESE, "jw" , 0 , 0 , "Javanese") - LNG(wxLANGUAGE_KANNADA, "kn" , LANG_KANNADA , SUBLANG_DEFAULT , "Kannada") - LNG(wxLANGUAGE_KASHMIRI, "ks" , LANG_KASHMIRI , SUBLANG_DEFAULT , "Kashmiri") - LNG(wxLANGUAGE_KASHMIRI_INDIA, "ks_IN", LANG_KASHMIRI , SUBLANG_KASHMIRI_INDIA , "Kashmiri (India)") - LNG(wxLANGUAGE_KAZAKH, "kk" , LANG_KAZAK , SUBLANG_DEFAULT , "Kazakh") - LNG(wxLANGUAGE_KERNEWEK, "kw_GB", 0 , 0 , "Kernewek") - LNG(wxLANGUAGE_KINYARWANDA, "rw" , 0 , 0 , "Kinyarwanda") - LNG(wxLANGUAGE_KIRGHIZ, "ky" , 0 , 0 , "Kirghiz") - LNG(wxLANGUAGE_KIRUNDI, "rn" , 0 , 0 , "Kirundi") - LNG(wxLANGUAGE_KONKANI, "" , LANG_KONKANI , SUBLANG_DEFAULT , "Konkani") - LNG(wxLANGUAGE_KOREAN, "ko_KR", LANG_KOREAN , SUBLANG_KOREAN , "Korean") - LNG(wxLANGUAGE_KURDISH, "ku" , 0 , 0 , "Kurdish") - LNG(wxLANGUAGE_LAOTHIAN, "lo" , 0 , 0 , "Laothian") - LNG(wxLANGUAGE_LATIN, "la" , 0 , 0 , "Latin") - LNG(wxLANGUAGE_LATVIAN, "lv_LV", LANG_LATVIAN , SUBLANG_DEFAULT , "Latvian") - LNG(wxLANGUAGE_LINGALA, "ln" , 0 , 0 , "Lingala") - LNG(wxLANGUAGE_LITHUANIAN, "lt_LT", LANG_LITHUANIAN, SUBLANG_LITHUANIAN , "Lithuanian") - LNG(wxLANGUAGE_MACEDONIAN, "mk_MK", LANG_MACEDONIAN, SUBLANG_DEFAULT , "Macedonian") - LNG(wxLANGUAGE_MALAGASY, "mg" , 0 , 0 , "Malagasy") - LNG(wxLANGUAGE_MALAY, "ms_MY", LANG_MALAY , SUBLANG_DEFAULT , "Malay") - LNG(wxLANGUAGE_MALAYALAM, "ml" , LANG_MALAYALAM , SUBLANG_DEFAULT , "Malayalam") - LNG(wxLANGUAGE_MALAY_BRUNEI_DARUSSALAM, "ms_BN", LANG_MALAY , SUBLANG_MALAY_BRUNEI_DARUSSALAM , "Malay (Brunei Darussalam)") - LNG(wxLANGUAGE_MALAY_MALAYSIA, "ms_MY", LANG_MALAY , SUBLANG_MALAY_MALAYSIA , "Malay (Malaysia)") - LNG(wxLANGUAGE_MALTESE, "mt_MT", 0 , 0 , "Maltese") - LNG(wxLANGUAGE_MANIPURI, "" , LANG_MANIPURI , SUBLANG_DEFAULT , "Manipuri") - LNG(wxLANGUAGE_MAORI, "mi" , 0 , 0 , "Maori") - LNG(wxLANGUAGE_MARATHI, "mr_IN", LANG_MARATHI , SUBLANG_DEFAULT , "Marathi") - LNG(wxLANGUAGE_MOLDAVIAN, "mo" , 0 , 0 , "Moldavian") - LNG(wxLANGUAGE_MONGOLIAN, "mn" , 0 , 0 , "Mongolian") - LNG(wxLANGUAGE_NAURU, "na" , 0 , 0 , "Nauru") - LNG(wxLANGUAGE_NEPALI, "ne" , LANG_NEPALI , SUBLANG_DEFAULT , "Nepali") - LNG(wxLANGUAGE_NEPALI_INDIA, "ne_IN", LANG_NEPALI , SUBLANG_NEPALI_INDIA , "Nepali (India)") - LNG(wxLANGUAGE_NORWEGIAN_BOKMAL, "no_NO", LANG_NORWEGIAN , SUBLANG_NORWEGIAN_BOKMAL , "Norwegian (Bokmal)") - LNG(wxLANGUAGE_NORWEGIAN_NYNORSK, "nn_NO", LANG_NORWEGIAN , SUBLANG_NORWEGIAN_NYNORSK , "Norwegian (Nynorsk)") - LNG(wxLANGUAGE_OCCITAN, "oc" , 0 , 0 , "Occitan") - LNG(wxLANGUAGE_ORIYA, "or" , LANG_ORIYA , SUBLANG_DEFAULT , "Oriya") - LNG(wxLANGUAGE_OROMO, "om" , 0 , 0 , "(Afan) Oromo") - LNG(wxLANGUAGE_PASHTO, "ps" , 0 , 0 , "Pashto, Pushto") - LNG(wxLANGUAGE_POLISH, "pl_PL", LANG_POLISH , SUBLANG_DEFAULT , "Polish") - LNG(wxLANGUAGE_PORTUGUESE, "pt_PT", LANG_PORTUGUESE, SUBLANG_PORTUGUESE , "Portuguese") - LNG(wxLANGUAGE_PORTUGUESE_BRAZILIAN, "pt_BR", LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN , "Portuguese (Brazilian)") - LNG(wxLANGUAGE_PUNJABI, "pa" , LANG_PUNJABI , SUBLANG_DEFAULT , "Punjabi") - LNG(wxLANGUAGE_QUECHUA, "qu" , 0 , 0 , "Quechua") - LNG(wxLANGUAGE_RHAETO_ROMANCE, "rm" , 0 , 0 , "Rhaeto-Romance") - LNG(wxLANGUAGE_ROMANIAN, "ro_RO", LANG_ROMANIAN , SUBLANG_DEFAULT , "Romanian") - LNG(wxLANGUAGE_RUSSIAN, "ru_RU", LANG_RUSSIAN , SUBLANG_DEFAULT , "Russian") - LNG(wxLANGUAGE_RUSSIAN_UKRAINE, "ru_UA", 0 , 0 , "Russian (Ukraine)") - LNG(wxLANGUAGE_SAMOAN, "sm" , 0 , 0 , "Samoan") - LNG(wxLANGUAGE_SANGHO, "sg" , 0 , 0 , "Sangho") - LNG(wxLANGUAGE_SANSKRIT, "sa" , LANG_SANSKRIT , SUBLANG_DEFAULT , "Sanskrit") - LNG(wxLANGUAGE_SCOTS_GAELIC, "gd" , 0 , 0 , "Scots Gaelic") - LNG(wxLANGUAGE_SERBIAN, "sr_YU", LANG_SERBIAN , SUBLANG_DEFAULT , "Serbian") - LNG(wxLANGUAGE_SERBIAN_CYRILLIC, "sr_YU", LANG_SERBIAN , SUBLANG_SERBIAN_CYRILLIC , "Serbian (Cyrillic)") - LNG(wxLANGUAGE_SERBIAN_LATIN, "sr_YU", LANG_SERBIAN , SUBLANG_SERBIAN_LATIN , "Serbian (Latin)") - LNG(wxLANGUAGE_SERBO_CROATIAN, "sh" , 0 , 0 , "Serbo-Croatian") - LNG(wxLANGUAGE_SESOTHO, "st" , 0 , 0 , "Sesotho") - LNG(wxLANGUAGE_SETSWANA, "tn" , 0 , 0 , "Setswana") - LNG(wxLANGUAGE_SHONA, "sn" , 0 , 0 , "Shona") - LNG(wxLANGUAGE_SINDHI, "sd" , LANG_SINDHI , SUBLANG_DEFAULT , "Sindhi") - LNG(wxLANGUAGE_SINHALESE, "si" , 0 , 0 , "Sinhalese") - LNG(wxLANGUAGE_SISWATI, "ss" , 0 , 0 , "Siswati") - LNG(wxLANGUAGE_SLOVAK, "sk_SK", LANG_SLOVAK , SUBLANG_DEFAULT , "Slovak") - LNG(wxLANGUAGE_SLOVENIAN, "sl_SI", LANG_SLOVENIAN , SUBLANG_DEFAULT , "Slovenian") - LNG(wxLANGUAGE_SOMALI, "so" , 0 , 0 , "Somali") - LNG(wxLANGUAGE_SPANISH, "es_ES", LANG_SPANISH , SUBLANG_SPANISH , "Spanish") - LNG(wxLANGUAGE_SPANISH_ARGENTINA, "es_AR", LANG_SPANISH , SUBLANG_SPANISH_ARGENTINA , "Spanish (Argentina)") - LNG(wxLANGUAGE_SPANISH_BOLIVIA, "es_BO", LANG_SPANISH , SUBLANG_SPANISH_BOLIVIA , "Spanish (Bolivia)") - LNG(wxLANGUAGE_SPANISH_CHILE, "es_CL", LANG_SPANISH , SUBLANG_SPANISH_CHILE , "Spanish (Chile)") - LNG(wxLANGUAGE_SPANISH_COLOMBIA, "es_CO", LANG_SPANISH , SUBLANG_SPANISH_COLOMBIA , "Spanish (Colombia)") - LNG(wxLANGUAGE_SPANISH_COSTA_RICA, "es_CR", LANG_SPANISH , SUBLANG_SPANISH_COSTA_RICA , "Spanish (Costa Rica)") - LNG(wxLANGUAGE_SPANISH_DOMINICAN_REPUBLIC, "es_DO", LANG_SPANISH , SUBLANG_SPANISH_DOMINICAN_REPUBLIC, "Spanish (Dominican republic)") - LNG(wxLANGUAGE_SPANISH_ECUADOR, "es_EC", LANG_SPANISH , SUBLANG_SPANISH_ECUADOR , "Spanish (Ecuador)") - LNG(wxLANGUAGE_SPANISH_EL_SALVADOR, "es_SV", LANG_SPANISH , SUBLANG_SPANISH_EL_SALVADOR , "Spanish (El Salvador)") - LNG(wxLANGUAGE_SPANISH_GUATEMALA, "es_GT", LANG_SPANISH , SUBLANG_SPANISH_GUATEMALA , "Spanish (Guatemala)") - LNG(wxLANGUAGE_SPANISH_HONDURAS, "es_HN", LANG_SPANISH , SUBLANG_SPANISH_HONDURAS , "Spanish (Honduras)") - LNG(wxLANGUAGE_SPANISH_MEXICAN, "es_MX", LANG_SPANISH , SUBLANG_SPANISH_MEXICAN , "Spanish (Mexican)") - LNG(wxLANGUAGE_SPANISH_MODERN, "es_ES", LANG_SPANISH , SUBLANG_SPANISH_MODERN , "Spanish (Modern)") - LNG(wxLANGUAGE_SPANISH_NICARAGUA, "es_NI", LANG_SPANISH , SUBLANG_SPANISH_NICARAGUA , "Spanish (Nicaragua)") - LNG(wxLANGUAGE_SPANISH_PANAMA, "es_PA", LANG_SPANISH , SUBLANG_SPANISH_PANAMA , "Spanish (Panama)") - LNG(wxLANGUAGE_SPANISH_PARAGUAY, "es_PY", LANG_SPANISH , SUBLANG_SPANISH_PARAGUAY , "Spanish (Paraguay)") - LNG(wxLANGUAGE_SPANISH_PERU, "es_PE", LANG_SPANISH , SUBLANG_SPANISH_PERU , "Spanish (Peru)") - LNG(wxLANGUAGE_SPANISH_PUERTO_RICO, "es_PR", LANG_SPANISH , SUBLANG_SPANISH_PUERTO_RICO , "Spanish (Puerto Rico)") - LNG(wxLANGUAGE_SPANISH_URUGUAY, "es_UY", LANG_SPANISH , SUBLANG_SPANISH_URUGUAY , "Spanish (Uruguay)") - LNG(wxLANGUAGE_SPANISH_US, "es_US", 0 , 0 , "Spanish (U.S.)") - LNG(wxLANGUAGE_SPANISH_VENEZUELA, "es_VE", LANG_SPANISH , SUBLANG_SPANISH_VENEZUELA , "Spanish (Venezuela)") - LNG(wxLANGUAGE_SUNDANESE, "su" , 0 , 0 , "Sundanese") - LNG(wxLANGUAGE_SWAHILI, "sw_KE", LANG_SWAHILI , SUBLANG_DEFAULT , "Swahili") - LNG(wxLANGUAGE_SWEDISH, "sv_SE", LANG_SWEDISH , SUBLANG_SWEDISH , "Swedish") - LNG(wxLANGUAGE_SWEDISH_FINLAND, "sv_FI", LANG_SWEDISH , SUBLANG_SWEDISH_FINLAND , "Swedish (Finland)") - LNG(wxLANGUAGE_TAGALOG, "tl" , 0 , 0 , "Tagalog") - LNG(wxLANGUAGE_TAJIK, "tg" , 0 , 0 , "Tajik") - LNG(wxLANGUAGE_TAMIL, "ta" , LANG_TAMIL , SUBLANG_DEFAULT , "Tamil") - LNG(wxLANGUAGE_TATAR, "tt" , LANG_TATAR , SUBLANG_DEFAULT , "Tatar") - LNG(wxLANGUAGE_TELUGU, "te" , LANG_TELUGU , SUBLANG_DEFAULT , "Telugu") - LNG(wxLANGUAGE_THAI, "th_TH", LANG_THAI , SUBLANG_DEFAULT , "Thai") - LNG(wxLANGUAGE_TIBETAN, "bo" , 0 , 0 , "Tibetan") - LNG(wxLANGUAGE_TIGRINYA, "ti" , 0 , 0 , "Tigrinya") - LNG(wxLANGUAGE_TONGA, "to" , 0 , 0 , "Tonga") - LNG(wxLANGUAGE_TSONGA, "ts" , 0 , 0 , "Tsonga") - LNG(wxLANGUAGE_TURKISH, "tr_TR", LANG_TURKISH , SUBLANG_DEFAULT , "Turkish") - LNG(wxLANGUAGE_TURKMEN, "tk" , 0 , 0 , "Turkmen") - LNG(wxLANGUAGE_TWI, "tw" , 0 , 0 , "Twi") - LNG(wxLANGUAGE_UIGHUR, "ug" , 0 , 0 , "Uighur") - LNG(wxLANGUAGE_UKRAINIAN, "uk_UA", LANG_UKRAINIAN , SUBLANG_DEFAULT , "Ukrainian") - LNG(wxLANGUAGE_URDU, "ur" , LANG_URDU , SUBLANG_DEFAULT , "Urdu") - LNG(wxLANGUAGE_URDU_INDIA, "ur_IN", LANG_URDU , SUBLANG_URDU_INDIA , "Urdu (India)") - LNG(wxLANGUAGE_URDU_PAKISTAN, "ur_PK", LANG_URDU , SUBLANG_URDU_PAKISTAN , "Urdu (Pakistan)") - LNG(wxLANGUAGE_UZBEK, "uz" , LANG_UZBEK , SUBLANG_DEFAULT , "Uzbek") - LNG(wxLANGUAGE_UZBEK_CYRILLIC, "uz" , LANG_UZBEK , SUBLANG_UZBEK_CYRILLIC , "Uzbek (Cyrillic)") - LNG(wxLANGUAGE_UZBEK_LATIN, "uz" , LANG_UZBEK , SUBLANG_UZBEK_LATIN , "Uzbek (Latin)") - LNG(wxLANGUAGE_VIETNAMESE, "vi_VN", LANG_VIETNAMESE, SUBLANG_DEFAULT , "Vietnamese") - LNG(wxLANGUAGE_VOLAPUK, "vo" , 0 , 0 , "Volapuk") - LNG(wxLANGUAGE_WELSH, "cy" , 0 , 0 , "Welsh") - LNG(wxLANGUAGE_WOLOF, "wo" , 0 , 0 , "Wolof") - LNG(wxLANGUAGE_XHOSA, "xh" , 0 , 0 , "Xhosa") - LNG(wxLANGUAGE_YIDDISH, "yi" , 0 , 0 , "Yiddish") - LNG(wxLANGUAGE_YORUBA, "yo" , 0 , 0 , "Yoruba") - LNG(wxLANGUAGE_ZHUANG, "za" , 0 , 0 , "Zhuang") - LNG(wxLANGUAGE_ZULU, "zu" , 0 , 0 , "Zulu") + LNG(wxLANGUAGE_ABKHAZIAN, "ab" , 0 , 0 , wxLayout_LeftToRight, "Abkhazian") + LNG(wxLANGUAGE_AFAR, "aa" , 0 , 0 , wxLayout_LeftToRight, "Afar") + LNG(wxLANGUAGE_AFRIKAANS, "af_ZA", LANG_AFRIKAANS , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Afrikaans") + LNG(wxLANGUAGE_ALBANIAN, "sq_AL", LANG_ALBANIAN , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Albanian") + LNG(wxLANGUAGE_AMHARIC, "am" , 0 , 0 , wxLayout_LeftToRight, "Amharic") + LNG(wxLANGUAGE_ARABIC, "ar" , LANG_ARABIC , SUBLANG_DEFAULT , wxLayout_RightToLeft, "Arabic") + LNG(wxLANGUAGE_ARABIC_ALGERIA, "ar_DZ", LANG_ARABIC , SUBLANG_ARABIC_ALGERIA , wxLayout_RightToLeft, "Arabic (Algeria)") + LNG(wxLANGUAGE_ARABIC_BAHRAIN, "ar_BH", LANG_ARABIC , SUBLANG_ARABIC_BAHRAIN , wxLayout_RightToLeft, "Arabic (Bahrain)") + LNG(wxLANGUAGE_ARABIC_EGYPT, "ar_EG", LANG_ARABIC , SUBLANG_ARABIC_EGYPT , wxLayout_RightToLeft, "Arabic (Egypt)") + LNG(wxLANGUAGE_ARABIC_IRAQ, "ar_IQ", LANG_ARABIC , SUBLANG_ARABIC_IRAQ , wxLayout_RightToLeft, "Arabic (Iraq)") + LNG(wxLANGUAGE_ARABIC_JORDAN, "ar_JO", LANG_ARABIC , SUBLANG_ARABIC_JORDAN , wxLayout_RightToLeft, "Arabic (Jordan)") + LNG(wxLANGUAGE_ARABIC_KUWAIT, "ar_KW", LANG_ARABIC , SUBLANG_ARABIC_KUWAIT , wxLayout_RightToLeft, "Arabic (Kuwait)") + LNG(wxLANGUAGE_ARABIC_LEBANON, "ar_LB", LANG_ARABIC , SUBLANG_ARABIC_LEBANON , wxLayout_RightToLeft, "Arabic (Lebanon)") + LNG(wxLANGUAGE_ARABIC_LIBYA, "ar_LY", LANG_ARABIC , SUBLANG_ARABIC_LIBYA , wxLayout_RightToLeft, "Arabic (Libya)") + LNG(wxLANGUAGE_ARABIC_MOROCCO, "ar_MA", LANG_ARABIC , SUBLANG_ARABIC_MOROCCO , wxLayout_RightToLeft, "Arabic (Morocco)") + LNG(wxLANGUAGE_ARABIC_OMAN, "ar_OM", LANG_ARABIC , SUBLANG_ARABIC_OMAN , wxLayout_RightToLeft, "Arabic (Oman)") + LNG(wxLANGUAGE_ARABIC_QATAR, "ar_QA", LANG_ARABIC , SUBLANG_ARABIC_QATAR , wxLayout_RightToLeft, "Arabic (Qatar)") + LNG(wxLANGUAGE_ARABIC_SAUDI_ARABIA, "ar_SA", LANG_ARABIC , SUBLANG_ARABIC_SAUDI_ARABIA , wxLayout_RightToLeft, "Arabic (Saudi Arabia)") + LNG(wxLANGUAGE_ARABIC_SUDAN, "ar_SD", 0 , 0 , wxLayout_RightToLeft, "Arabic (Sudan)") + LNG(wxLANGUAGE_ARABIC_SYRIA, "ar_SY", LANG_ARABIC , SUBLANG_ARABIC_SYRIA , wxLayout_RightToLeft, "Arabic (Syria)") + LNG(wxLANGUAGE_ARABIC_TUNISIA, "ar_TN", LANG_ARABIC , SUBLANG_ARABIC_TUNISIA , wxLayout_RightToLeft, "Arabic (Tunisia)") + LNG(wxLANGUAGE_ARABIC_UAE, "ar_AE", LANG_ARABIC , SUBLANG_ARABIC_UAE , wxLayout_RightToLeft, "Arabic (Uae)") + LNG(wxLANGUAGE_ARABIC_YEMEN, "ar_YE", LANG_ARABIC , SUBLANG_ARABIC_YEMEN , wxLayout_RightToLeft, "Arabic (Yemen)") + LNG(wxLANGUAGE_ARMENIAN, "hy" , LANG_ARMENIAN , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Armenian") + LNG(wxLANGUAGE_ASSAMESE, "as" , LANG_ASSAMESE , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Assamese") + LNG(wxLANGUAGE_ASTURIAN, "ast" , 0 , 0 , wxLayout_LeftToRight, "Asturian") + LNG(wxLANGUAGE_AYMARA, "ay" , 0 , 0 , wxLayout_LeftToRight, "Aymara") + LNG(wxLANGUAGE_AZERI, "az" , LANG_AZERI , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Azeri") + LNG(wxLANGUAGE_AZERI_CYRILLIC, "az" , LANG_AZERI , SUBLANG_AZERI_CYRILLIC , wxLayout_LeftToRight, "Azeri (Cyrillic)") + LNG(wxLANGUAGE_AZERI_LATIN, "az" , LANG_AZERI , SUBLANG_AZERI_LATIN , wxLayout_LeftToRight, "Azeri (Latin)") + LNG(wxLANGUAGE_BASHKIR, "ba" , 0 , 0 , wxLayout_LeftToRight, "Bashkir") + LNG(wxLANGUAGE_BASQUE, "eu_ES", LANG_BASQUE , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Basque") + LNG(wxLANGUAGE_BELARUSIAN, "be_BY", LANG_BELARUSIAN, SUBLANG_DEFAULT , wxLayout_LeftToRight, "Belarusian") + LNG(wxLANGUAGE_BENGALI, "bn" , LANG_BENGALI , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Bengali") + LNG(wxLANGUAGE_BHUTANI, "dz" , 0 , 0 , wxLayout_LeftToRight, "Bhutani") + LNG(wxLANGUAGE_BIHARI, "bh" , 0 , 0 , wxLayout_LeftToRight, "Bihari") + LNG(wxLANGUAGE_BISLAMA, "bi" , 0 , 0 , wxLayout_LeftToRight, "Bislama") + LNG(wxLANGUAGE_BRETON, "br" , 0 , 0 , wxLayout_LeftToRight, "Breton") + LNG(wxLANGUAGE_BULGARIAN, "bg_BG", LANG_BULGARIAN , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Bulgarian") + LNG(wxLANGUAGE_BURMESE, "my" , 0 , 0 , wxLayout_LeftToRight, "Burmese") + LNG(wxLANGUAGE_CAMBODIAN, "km" , 0 , 0 , wxLayout_LeftToRight, "Cambodian") + LNG(wxLANGUAGE_CATALAN, "ca_ES", LANG_CATALAN , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Catalan") + LNG(wxLANGUAGE_CHINESE, "zh_TW", LANG_CHINESE , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Chinese") + LNG(wxLANGUAGE_CHINESE_SIMPLIFIED, "zh_CN", LANG_CHINESE , SUBLANG_CHINESE_SIMPLIFIED , wxLayout_LeftToRight, "Chinese (Simplified)") + LNG(wxLANGUAGE_CHINESE_TRADITIONAL, "zh_TW", LANG_CHINESE , SUBLANG_CHINESE_TRADITIONAL , wxLayout_LeftToRight, "Chinese (Traditional)") + LNG(wxLANGUAGE_CHINESE_HONGKONG, "zh_HK", LANG_CHINESE , SUBLANG_CHINESE_HONGKONG , wxLayout_LeftToRight, "Chinese (Hongkong)") + LNG(wxLANGUAGE_CHINESE_MACAU, "zh_MO", LANG_CHINESE , SUBLANG_CHINESE_MACAU , wxLayout_LeftToRight, "Chinese (Macau)") + LNG(wxLANGUAGE_CHINESE_SINGAPORE, "zh_SG", LANG_CHINESE , SUBLANG_CHINESE_SINGAPORE , wxLayout_LeftToRight, "Chinese (Singapore)") + LNG(wxLANGUAGE_CHINESE_TAIWAN, "zh_TW", LANG_CHINESE , SUBLANG_CHINESE_TRADITIONAL , wxLayout_LeftToRight, "Chinese (Taiwan)") + LNG(wxLANGUAGE_CORSICAN, "co" , 0 , 0 , wxLayout_LeftToRight, "Corsican") + LNG(wxLANGUAGE_CROATIAN, "hr_HR", LANG_CROATIAN , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Croatian") + LNG(wxLANGUAGE_CZECH, "cs_CZ", LANG_CZECH , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Czech") + LNG(wxLANGUAGE_DANISH, "da_DK", LANG_DANISH , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Danish") + LNG(wxLANGUAGE_DUTCH, "nl_NL", LANG_DUTCH , SUBLANG_DUTCH , wxLayout_LeftToRight, "Dutch") + LNG(wxLANGUAGE_DUTCH_BELGIAN, "nl_BE", LANG_DUTCH , SUBLANG_DUTCH_BELGIAN , wxLayout_LeftToRight, "Dutch (Belgian)") + LNG(wxLANGUAGE_ENGLISH, "en_GB", LANG_ENGLISH , SUBLANG_ENGLISH_UK , wxLayout_LeftToRight, "English") + LNG(wxLANGUAGE_ENGLISH_UK, "en_GB", LANG_ENGLISH , SUBLANG_ENGLISH_UK , wxLayout_LeftToRight, "English (U.K.)") + LNG(wxLANGUAGE_ENGLISH_US, "en_US", LANG_ENGLISH , SUBLANG_ENGLISH_US , wxLayout_LeftToRight, "English (U.S.)") + LNG(wxLANGUAGE_ENGLISH_AUSTRALIA, "en_AU", LANG_ENGLISH , SUBLANG_ENGLISH_AUS , wxLayout_LeftToRight, "English (Australia)") + LNG(wxLANGUAGE_ENGLISH_BELIZE, "en_BZ", LANG_ENGLISH , SUBLANG_ENGLISH_BELIZE , wxLayout_LeftToRight, "English (Belize)") + LNG(wxLANGUAGE_ENGLISH_BOTSWANA, "en_BW", 0 , 0 , wxLayout_LeftToRight, "English (Botswana)") + LNG(wxLANGUAGE_ENGLISH_CANADA, "en_CA", LANG_ENGLISH , SUBLANG_ENGLISH_CAN , wxLayout_LeftToRight, "English (Canada)") + LNG(wxLANGUAGE_ENGLISH_CARIBBEAN, "en_CB", LANG_ENGLISH , SUBLANG_ENGLISH_CARIBBEAN , wxLayout_LeftToRight, "English (Caribbean)") + LNG(wxLANGUAGE_ENGLISH_DENMARK, "en_DK", 0 , 0 , wxLayout_LeftToRight, "English (Denmark)") + LNG(wxLANGUAGE_ENGLISH_EIRE, "en_IE", LANG_ENGLISH , SUBLANG_ENGLISH_EIRE , wxLayout_LeftToRight, "English (Eire)") + LNG(wxLANGUAGE_ENGLISH_JAMAICA, "en_JM", LANG_ENGLISH , SUBLANG_ENGLISH_JAMAICA , wxLayout_LeftToRight, "English (Jamaica)") + LNG(wxLANGUAGE_ENGLISH_NEW_ZEALAND, "en_NZ", LANG_ENGLISH , SUBLANG_ENGLISH_NZ , wxLayout_LeftToRight, "English (New Zealand)") + LNG(wxLANGUAGE_ENGLISH_PHILIPPINES, "en_PH", LANG_ENGLISH , SUBLANG_ENGLISH_PHILIPPINES , wxLayout_LeftToRight, "English (Philippines)") + LNG(wxLANGUAGE_ENGLISH_SOUTH_AFRICA, "en_ZA", LANG_ENGLISH , SUBLANG_ENGLISH_SOUTH_AFRICA , wxLayout_LeftToRight, "English (South Africa)") + LNG(wxLANGUAGE_ENGLISH_TRINIDAD, "en_TT", LANG_ENGLISH , SUBLANG_ENGLISH_TRINIDAD , wxLayout_LeftToRight, "English (Trinidad)") + LNG(wxLANGUAGE_ENGLISH_ZIMBABWE, "en_ZW", LANG_ENGLISH , SUBLANG_ENGLISH_ZIMBABWE , wxLayout_LeftToRight, "English (Zimbabwe)") + LNG(wxLANGUAGE_ESPERANTO, "eo" , 0 , 0 , wxLayout_LeftToRight, "Esperanto") + LNG(wxLANGUAGE_ESTONIAN, "et_EE", LANG_ESTONIAN , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Estonian") + LNG(wxLANGUAGE_FAEROESE, "fo_FO", LANG_FAEROESE , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Faeroese") + LNG(wxLANGUAGE_FARSI, "fa_IR", LANG_FARSI , SUBLANG_DEFAULT , wxLayout_RightToLeft, "Farsi") + LNG(wxLANGUAGE_FIJI, "fj" , 0 , 0 , wxLayout_LeftToRight, "Fiji") + LNG(wxLANGUAGE_FINNISH, "fi_FI", LANG_FINNISH , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Finnish") + LNG(wxLANGUAGE_FRENCH, "fr_FR", LANG_FRENCH , SUBLANG_FRENCH , wxLayout_LeftToRight, "French") + LNG(wxLANGUAGE_FRENCH_BELGIAN, "fr_BE", LANG_FRENCH , SUBLANG_FRENCH_BELGIAN , wxLayout_LeftToRight, "French (Belgian)") + LNG(wxLANGUAGE_FRENCH_CANADIAN, "fr_CA", LANG_FRENCH , SUBLANG_FRENCH_CANADIAN , wxLayout_LeftToRight, "French (Canadian)") + LNG(wxLANGUAGE_FRENCH_LUXEMBOURG, "fr_LU", LANG_FRENCH , SUBLANG_FRENCH_LUXEMBOURG , wxLayout_LeftToRight, "French (Luxembourg)") + LNG(wxLANGUAGE_FRENCH_MONACO, "fr_MC", LANG_FRENCH , SUBLANG_FRENCH_MONACO , wxLayout_LeftToRight, "French (Monaco)") + LNG(wxLANGUAGE_FRENCH_SWISS, "fr_CH", LANG_FRENCH , SUBLANG_FRENCH_SWISS , wxLayout_LeftToRight, "French (Swiss)") + LNG(wxLANGUAGE_FRISIAN, "fy" , 0 , 0 , wxLayout_LeftToRight, "Frisian") + LNG(wxLANGUAGE_GALICIAN, "gl_ES", 0 , 0 , wxLayout_LeftToRight, "Galician") + LNG(wxLANGUAGE_GEORGIAN, "ka_GE", LANG_GEORGIAN , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Georgian") + LNG(wxLANGUAGE_GERMAN, "de_DE", LANG_GERMAN , SUBLANG_GERMAN , wxLayout_LeftToRight, "German") + LNG(wxLANGUAGE_GERMAN_AUSTRIAN, "de_AT", LANG_GERMAN , SUBLANG_GERMAN_AUSTRIAN , wxLayout_LeftToRight, "German (Austrian)") + LNG(wxLANGUAGE_GERMAN_BELGIUM, "de_BE", 0 , 0 , wxLayout_LeftToRight, "German (Belgium)") + LNG(wxLANGUAGE_GERMAN_LIECHTENSTEIN, "de_LI", LANG_GERMAN , SUBLANG_GERMAN_LIECHTENSTEIN , wxLayout_LeftToRight, "German (Liechtenstein)") + LNG(wxLANGUAGE_GERMAN_LUXEMBOURG, "de_LU", LANG_GERMAN , SUBLANG_GERMAN_LUXEMBOURG , wxLayout_LeftToRight, "German (Luxembourg)") + LNG(wxLANGUAGE_GERMAN_SWISS, "de_CH", LANG_GERMAN , SUBLANG_GERMAN_SWISS , wxLayout_LeftToRight, "German (Swiss)") + LNG(wxLANGUAGE_GREEK, "el_GR", LANG_GREEK , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Greek") + LNG(wxLANGUAGE_GREENLANDIC, "kl_GL", 0 , 0 , wxLayout_LeftToRight, "Greenlandic") + LNG(wxLANGUAGE_GUARANI, "gn" , 0 , 0 , wxLayout_LeftToRight, "Guarani") + LNG(wxLANGUAGE_GUJARATI, "gu" , LANG_GUJARATI , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Gujarati") + LNG(wxLANGUAGE_HAUSA, "ha" , 0 , 0 , wxLayout_LeftToRight, "Hausa") + LNG(wxLANGUAGE_HEBREW, "he_IL", LANG_HEBREW , SUBLANG_DEFAULT , wxLayout_RightToLeft, "Hebrew") + LNG(wxLANGUAGE_HINDI, "hi_IN", LANG_HINDI , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Hindi") + LNG(wxLANGUAGE_HUNGARIAN, "hu_HU", LANG_HUNGARIAN , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Hungarian") + LNG(wxLANGUAGE_ICELANDIC, "is_IS", LANG_ICELANDIC , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Icelandic") + LNG(wxLANGUAGE_INDONESIAN, "id_ID", LANG_INDONESIAN, SUBLANG_DEFAULT , wxLayout_LeftToRight, "Indonesian") + LNG(wxLANGUAGE_INTERLINGUA, "ia" , 0 , 0 , wxLayout_LeftToRight, "Interlingua") + LNG(wxLANGUAGE_INTERLINGUE, "ie" , 0 , 0 , wxLayout_LeftToRight, "Interlingue") + LNG(wxLANGUAGE_INUKTITUT, "iu" , 0 , 0 , wxLayout_LeftToRight, "Inuktitut") + LNG(wxLANGUAGE_INUPIAK, "ik" , 0 , 0 , wxLayout_LeftToRight, "Inupiak") + LNG(wxLANGUAGE_IRISH, "ga_IE", 0 , 0 , wxLayout_LeftToRight, "Irish") + LNG(wxLANGUAGE_ITALIAN, "it_IT", LANG_ITALIAN , SUBLANG_ITALIAN , wxLayout_LeftToRight, "Italian") + LNG(wxLANGUAGE_ITALIAN_SWISS, "it_CH", LANG_ITALIAN , SUBLANG_ITALIAN_SWISS , wxLayout_LeftToRight, "Italian (Swiss)") + LNG(wxLANGUAGE_JAPANESE, "ja_JP", LANG_JAPANESE , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Japanese") + LNG(wxLANGUAGE_JAVANESE, "jw" , 0 , 0 , wxLayout_LeftToRight, "Javanese") + LNG(wxLANGUAGE_KANNADA, "kn" , LANG_KANNADA , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Kannada") + LNG(wxLANGUAGE_KASHMIRI, "ks" , LANG_KASHMIRI , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Kashmiri") + LNG(wxLANGUAGE_KASHMIRI_INDIA, "ks_IN", LANG_KASHMIRI , SUBLANG_KASHMIRI_INDIA , wxLayout_LeftToRight, "Kashmiri (India)") + LNG(wxLANGUAGE_KAZAKH, "kk" , LANG_KAZAK , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Kazakh") + LNG(wxLANGUAGE_KERNEWEK, "kw_GB", 0 , 0 , wxLayout_LeftToRight, "Kernewek") + LNG(wxLANGUAGE_KINYARWANDA, "rw" , 0 , 0 , wxLayout_LeftToRight, "Kinyarwanda") + LNG(wxLANGUAGE_KIRGHIZ, "ky" , 0 , 0 , wxLayout_LeftToRight, "Kirghiz") + LNG(wxLANGUAGE_KIRUNDI, "rn" , 0 , 0 , wxLayout_LeftToRight, "Kirundi") + LNG(wxLANGUAGE_KONKANI, "" , LANG_KONKANI , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Konkani") + LNG(wxLANGUAGE_KOREAN, "ko_KR", LANG_KOREAN , SUBLANG_KOREAN , wxLayout_LeftToRight, "Korean") + LNG(wxLANGUAGE_KURDISH, "ku_TR", 0 , 0 , wxLayout_LeftToRight, "Kurdish") + LNG(wxLANGUAGE_LAOTHIAN, "lo" , 0 , 0 , wxLayout_LeftToRight, "Laothian") + LNG(wxLANGUAGE_LATIN, "la" , 0 , 0 , wxLayout_LeftToRight, "Latin") + LNG(wxLANGUAGE_LATVIAN, "lv_LV", LANG_LATVIAN , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Latvian") + LNG(wxLANGUAGE_LINGALA, "ln" , 0 , 0 , wxLayout_LeftToRight, "Lingala") + LNG(wxLANGUAGE_LITHUANIAN, "lt_LT", LANG_LITHUANIAN, SUBLANG_LITHUANIAN , wxLayout_LeftToRight, "Lithuanian") + LNG(wxLANGUAGE_MACEDONIAN, "mk_MK", LANG_MACEDONIAN, SUBLANG_DEFAULT , wxLayout_LeftToRight, "Macedonian") + LNG(wxLANGUAGE_MALAGASY, "mg" , 0 , 0 , wxLayout_LeftToRight, "Malagasy") + LNG(wxLANGUAGE_MALAY, "ms_MY", LANG_MALAY , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Malay") + LNG(wxLANGUAGE_MALAYALAM, "ml" , LANG_MALAYALAM , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Malayalam") + LNG(wxLANGUAGE_MALAY_BRUNEI_DARUSSALAM, "ms_BN", LANG_MALAY , SUBLANG_MALAY_BRUNEI_DARUSSALAM , wxLayout_LeftToRight, "Malay (Brunei Darussalam)") + LNG(wxLANGUAGE_MALAY_MALAYSIA, "ms_MY", LANG_MALAY , SUBLANG_MALAY_MALAYSIA , wxLayout_LeftToRight, "Malay (Malaysia)") + LNG(wxLANGUAGE_MALTESE, "mt_MT", 0 , 0 , wxLayout_LeftToRight, "Maltese") + LNG(wxLANGUAGE_MANIPURI, "" , LANG_MANIPURI , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Manipuri") + LNG(wxLANGUAGE_MAORI, "mi" , 0 , 0 , wxLayout_LeftToRight, "Maori") + LNG(wxLANGUAGE_MARATHI, "mr_IN", LANG_MARATHI , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Marathi") + LNG(wxLANGUAGE_MOLDAVIAN, "mo" , 0 , 0 , wxLayout_LeftToRight, "Moldavian") + LNG(wxLANGUAGE_MONGOLIAN, "mn" , 0 , 0 , wxLayout_LeftToRight, "Mongolian") + LNG(wxLANGUAGE_NAURU, "na" , 0 , 0 , wxLayout_LeftToRight, "Nauru") + LNG(wxLANGUAGE_NEPALI, "ne_NP", LANG_NEPALI , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Nepali") + LNG(wxLANGUAGE_NEPALI_INDIA, "ne_IN", LANG_NEPALI , SUBLANG_NEPALI_INDIA , wxLayout_LeftToRight, "Nepali (India)") + LNG(wxLANGUAGE_NORWEGIAN_BOKMAL, "nb_NO", LANG_NORWEGIAN , SUBLANG_NORWEGIAN_BOKMAL , wxLayout_LeftToRight, "Norwegian (Bokmal)") + LNG(wxLANGUAGE_NORWEGIAN_NYNORSK, "nn_NO", LANG_NORWEGIAN , SUBLANG_NORWEGIAN_NYNORSK , wxLayout_LeftToRight, "Norwegian (Nynorsk)") + LNG(wxLANGUAGE_OCCITAN, "oc" , 0 , 0 , wxLayout_LeftToRight, "Occitan") + LNG(wxLANGUAGE_ORIYA, "or" , LANG_ORIYA , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Oriya") + LNG(wxLANGUAGE_OROMO, "om" , 0 , 0 , wxLayout_LeftToRight, "(Afan) Oromo") + LNG(wxLANGUAGE_PASHTO, "ps" , 0 , 0 , wxLayout_LeftToRight, "Pashto, Pushto") + LNG(wxLANGUAGE_POLISH, "pl_PL", LANG_POLISH , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Polish") + LNG(wxLANGUAGE_PORTUGUESE, "pt_PT", LANG_PORTUGUESE, SUBLANG_PORTUGUESE , wxLayout_LeftToRight, "Portuguese") + LNG(wxLANGUAGE_PORTUGUESE_BRAZILIAN, "pt_BR", LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN , wxLayout_LeftToRight, "Portuguese (Brazilian)") + LNG(wxLANGUAGE_PUNJABI, "pa" , LANG_PUNJABI , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Punjabi") + LNG(wxLANGUAGE_QUECHUA, "qu" , 0 , 0 , wxLayout_LeftToRight, "Quechua") + LNG(wxLANGUAGE_RHAETO_ROMANCE, "rm" , 0 , 0 , wxLayout_LeftToRight, "Rhaeto-Romance") + LNG(wxLANGUAGE_ROMANIAN, "ro_RO", LANG_ROMANIAN , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Romanian") + LNG(wxLANGUAGE_RUSSIAN, "ru_RU", LANG_RUSSIAN , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Russian") + LNG(wxLANGUAGE_RUSSIAN_UKRAINE, "ru_UA", 0 , 0 , wxLayout_LeftToRight, "Russian (Ukraine)") + LNG(wxLANGUAGE_SAMI, "se_NO", LANG_SAMI , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Northern Sami") + LNG(wxLANGUAGE_SAMOAN, "sm" , 0 , 0 , wxLayout_LeftToRight, "Samoan") + LNG(wxLANGUAGE_SANGHO, "sg" , 0 , 0 , wxLayout_LeftToRight, "Sangho") + LNG(wxLANGUAGE_SANSKRIT, "sa" , LANG_SANSKRIT , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Sanskrit") + LNG(wxLANGUAGE_SCOTS_GAELIC, "gd" , 0 , 0 , wxLayout_LeftToRight, "Scots Gaelic") + LNG(wxLANGUAGE_SERBIAN, "sr_RS", LANG_SERBIAN , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Serbian") + LNG(wxLANGUAGE_SERBIAN_CYRILLIC, "sr_RS", LANG_SERBIAN , SUBLANG_SERBIAN_CYRILLIC , wxLayout_LeftToRight, "Serbian (Cyrillic)") + LNG(wxLANGUAGE_SERBIAN_LATIN, "sr_RS@latin", LANG_SERBIAN , SUBLANG_SERBIAN_LATIN , wxLayout_LeftToRight, "Serbian (Latin)") + LNG(wxLANGUAGE_SERBIAN_CYRILLIC, "sr_YU", LANG_SERBIAN , SUBLANG_SERBIAN_CYRILLIC , wxLayout_LeftToRight, "Serbian (Cyrillic)") + LNG(wxLANGUAGE_SERBIAN_LATIN, "sr_YU@latin", LANG_SERBIAN , SUBLANG_SERBIAN_LATIN , wxLayout_LeftToRight, "Serbian (Latin)") + LNG(wxLANGUAGE_SERBO_CROATIAN, "sh" , 0 , 0 , wxLayout_LeftToRight, "Serbo-Croatian") + LNG(wxLANGUAGE_SESOTHO, "st" , 0 , 0 , wxLayout_LeftToRight, "Sesotho") + LNG(wxLANGUAGE_SETSWANA, "tn" , 0 , 0 , wxLayout_LeftToRight, "Setswana") + LNG(wxLANGUAGE_SHONA, "sn" , 0 , 0 , wxLayout_LeftToRight, "Shona") + LNG(wxLANGUAGE_SINDHI, "sd" , LANG_SINDHI , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Sindhi") + LNG(wxLANGUAGE_SINHALESE, "si" , 0 , 0 , wxLayout_LeftToRight, "Sinhalese") + LNG(wxLANGUAGE_SISWATI, "ss" , 0 , 0 , wxLayout_LeftToRight, "Siswati") + LNG(wxLANGUAGE_SLOVAK, "sk_SK", LANG_SLOVAK , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Slovak") + LNG(wxLANGUAGE_SLOVENIAN, "sl_SI", LANG_SLOVENIAN , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Slovenian") + LNG(wxLANGUAGE_SOMALI, "so" , 0 , 0 , wxLayout_LeftToRight, "Somali") + LNG(wxLANGUAGE_SPANISH, "es_ES", LANG_SPANISH , SUBLANG_SPANISH , wxLayout_LeftToRight, "Spanish") + LNG(wxLANGUAGE_SPANISH_ARGENTINA, "es_AR", LANG_SPANISH , SUBLANG_SPANISH_ARGENTINA , wxLayout_LeftToRight, "Spanish (Argentina)") + LNG(wxLANGUAGE_SPANISH_BOLIVIA, "es_BO", LANG_SPANISH , SUBLANG_SPANISH_BOLIVIA , wxLayout_LeftToRight, "Spanish (Bolivia)") + LNG(wxLANGUAGE_SPANISH_CHILE, "es_CL", LANG_SPANISH , SUBLANG_SPANISH_CHILE , wxLayout_LeftToRight, "Spanish (Chile)") + LNG(wxLANGUAGE_SPANISH_COLOMBIA, "es_CO", LANG_SPANISH , SUBLANG_SPANISH_COLOMBIA , wxLayout_LeftToRight, "Spanish (Colombia)") + LNG(wxLANGUAGE_SPANISH_COSTA_RICA, "es_CR", LANG_SPANISH , SUBLANG_SPANISH_COSTA_RICA , wxLayout_LeftToRight, "Spanish (Costa Rica)") + LNG(wxLANGUAGE_SPANISH_DOMINICAN_REPUBLIC, "es_DO", LANG_SPANISH , SUBLANG_SPANISH_DOMINICAN_REPUBLIC, wxLayout_LeftToRight, "Spanish (Dominican republic)") + LNG(wxLANGUAGE_SPANISH_ECUADOR, "es_EC", LANG_SPANISH , SUBLANG_SPANISH_ECUADOR , wxLayout_LeftToRight, "Spanish (Ecuador)") + LNG(wxLANGUAGE_SPANISH_EL_SALVADOR, "es_SV", LANG_SPANISH , SUBLANG_SPANISH_EL_SALVADOR , wxLayout_LeftToRight, "Spanish (El Salvador)") + LNG(wxLANGUAGE_SPANISH_GUATEMALA, "es_GT", LANG_SPANISH , SUBLANG_SPANISH_GUATEMALA , wxLayout_LeftToRight, "Spanish (Guatemala)") + LNG(wxLANGUAGE_SPANISH_HONDURAS, "es_HN", LANG_SPANISH , SUBLANG_SPANISH_HONDURAS , wxLayout_LeftToRight, "Spanish (Honduras)") + LNG(wxLANGUAGE_SPANISH_MEXICAN, "es_MX", LANG_SPANISH , SUBLANG_SPANISH_MEXICAN , wxLayout_LeftToRight, "Spanish (Mexican)") + LNG(wxLANGUAGE_SPANISH_MODERN, "es_ES", LANG_SPANISH , SUBLANG_SPANISH_MODERN , wxLayout_LeftToRight, "Spanish (Modern)") + LNG(wxLANGUAGE_SPANISH_NICARAGUA, "es_NI", LANG_SPANISH , SUBLANG_SPANISH_NICARAGUA , wxLayout_LeftToRight, "Spanish (Nicaragua)") + LNG(wxLANGUAGE_SPANISH_PANAMA, "es_PA", LANG_SPANISH , SUBLANG_SPANISH_PANAMA , wxLayout_LeftToRight, "Spanish (Panama)") + LNG(wxLANGUAGE_SPANISH_PARAGUAY, "es_PY", LANG_SPANISH , SUBLANG_SPANISH_PARAGUAY , wxLayout_LeftToRight, "Spanish (Paraguay)") + LNG(wxLANGUAGE_SPANISH_PERU, "es_PE", LANG_SPANISH , SUBLANG_SPANISH_PERU , wxLayout_LeftToRight, "Spanish (Peru)") + LNG(wxLANGUAGE_SPANISH_PUERTO_RICO, "es_PR", LANG_SPANISH , SUBLANG_SPANISH_PUERTO_RICO , wxLayout_LeftToRight, "Spanish (Puerto Rico)") + LNG(wxLANGUAGE_SPANISH_URUGUAY, "es_UY", LANG_SPANISH , SUBLANG_SPANISH_URUGUAY , wxLayout_LeftToRight, "Spanish (Uruguay)") + LNG(wxLANGUAGE_SPANISH_US, "es_US", 0 , 0 , wxLayout_LeftToRight, "Spanish (U.S.)") + LNG(wxLANGUAGE_SPANISH_VENEZUELA, "es_VE", LANG_SPANISH , SUBLANG_SPANISH_VENEZUELA , wxLayout_LeftToRight, "Spanish (Venezuela)") + LNG(wxLANGUAGE_SUNDANESE, "su" , 0 , 0 , wxLayout_LeftToRight, "Sundanese") + LNG(wxLANGUAGE_SWAHILI, "sw_KE", LANG_SWAHILI , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Swahili") + LNG(wxLANGUAGE_SWEDISH, "sv_SE", LANG_SWEDISH , SUBLANG_SWEDISH , wxLayout_LeftToRight, "Swedish") + LNG(wxLANGUAGE_SWEDISH_FINLAND, "sv_FI", LANG_SWEDISH , SUBLANG_SWEDISH_FINLAND , wxLayout_LeftToRight, "Swedish (Finland)") + LNG(wxLANGUAGE_TAGALOG, "tl_PH", 0 , 0 , wxLayout_LeftToRight, "Tagalog") + LNG(wxLANGUAGE_TAJIK, "tg" , 0 , 0 , wxLayout_LeftToRight, "Tajik") + LNG(wxLANGUAGE_TAMIL, "ta" , LANG_TAMIL , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Tamil") + LNG(wxLANGUAGE_TATAR, "tt" , LANG_TATAR , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Tatar") + LNG(wxLANGUAGE_TELUGU, "te" , LANG_TELUGU , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Telugu") + LNG(wxLANGUAGE_THAI, "th_TH", LANG_THAI , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Thai") + LNG(wxLANGUAGE_TIBETAN, "bo" , 0 , 0 , wxLayout_LeftToRight, "Tibetan") + LNG(wxLANGUAGE_TIGRINYA, "ti" , 0 , 0 , wxLayout_LeftToRight, "Tigrinya") + LNG(wxLANGUAGE_TONGA, "to" , 0 , 0 , wxLayout_LeftToRight, "Tonga") + LNG(wxLANGUAGE_TSONGA, "ts" , 0 , 0 , wxLayout_LeftToRight, "Tsonga") + LNG(wxLANGUAGE_TURKISH, "tr_TR", LANG_TURKISH , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Turkish") + LNG(wxLANGUAGE_TURKMEN, "tk" , 0 , 0 , wxLayout_LeftToRight, "Turkmen") + LNG(wxLANGUAGE_TWI, "tw" , 0 , 0 , wxLayout_LeftToRight, "Twi") + LNG(wxLANGUAGE_UIGHUR, "ug" , 0 , 0 , wxLayout_LeftToRight, "Uighur") + LNG(wxLANGUAGE_UKRAINIAN, "uk_UA", LANG_UKRAINIAN , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Ukrainian") + LNG(wxLANGUAGE_URDU, "ur" , LANG_URDU , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Urdu") + LNG(wxLANGUAGE_URDU_INDIA, "ur_IN", LANG_URDU , SUBLANG_URDU_INDIA , wxLayout_LeftToRight, "Urdu (India)") + LNG(wxLANGUAGE_URDU_PAKISTAN, "ur_PK", LANG_URDU , SUBLANG_URDU_PAKISTAN , wxLayout_LeftToRight, "Urdu (Pakistan)") + LNG(wxLANGUAGE_UZBEK, "uz" , LANG_UZBEK , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Uzbek") + LNG(wxLANGUAGE_UZBEK_CYRILLIC, "uz" , LANG_UZBEK , SUBLANG_UZBEK_CYRILLIC , wxLayout_LeftToRight, "Uzbek (Cyrillic)") + LNG(wxLANGUAGE_UZBEK_LATIN, "uz" , LANG_UZBEK , SUBLANG_UZBEK_LATIN , wxLayout_LeftToRight, "Uzbek (Latin)") + LNG(wxLANGUAGE_VALENCIAN, "ca_ES@valencia", 0 , 0 , wxLayout_LeftToRight, "Valencian (Southern Catalan)") + LNG(wxLANGUAGE_VIETNAMESE, "vi_VN", LANG_VIETNAMESE, SUBLANG_DEFAULT , wxLayout_LeftToRight, "Vietnamese") + LNG(wxLANGUAGE_VOLAPUK, "vo" , 0 , 0 , wxLayout_LeftToRight, "Volapuk") + LNG(wxLANGUAGE_WELSH, "cy" , 0 , 0 , wxLayout_LeftToRight, "Welsh") + LNG(wxLANGUAGE_WOLOF, "wo" , 0 , 0 , wxLayout_LeftToRight, "Wolof") + LNG(wxLANGUAGE_XHOSA, "xh" , 0 , 0 , wxLayout_LeftToRight, "Xhosa") + LNG(wxLANGUAGE_YIDDISH, "yi" , 0 , 0 , wxLayout_LeftToRight, "Yiddish") + LNG(wxLANGUAGE_YORUBA, "yo" , 0 , 0 , wxLayout_LeftToRight, "Yoruba") + LNG(wxLANGUAGE_ZHUANG, "za" , 0 , 0 , wxLayout_LeftToRight, "Zhuang") + LNG(wxLANGUAGE_ZULU, "zu" , 0 , 0 , wxLayout_LeftToRight, "Zulu") -}; +} #undef LNG // --- --- --- generated code ends here --- --- --- #endif // wxUSE_INTL -