X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/63986ba653aa30be8bde24c21feabb8fc6067d03..807a903e6807ae3e49928bda488a04bc0d832149:/src/common/intl.cpp diff --git a/src/common/intl.cpp b/src/common/intl.cpp index 032bfb30c1..be3b4f65b9 100644 --- a/src/common/intl.cpp +++ b/src/common/intl.cpp @@ -44,6 +44,7 @@ #include "wx/debug.h" #include "wx/utils.h" #include "wx/dynarray.h" +#include "wx/module.h" #ifdef __WIN32__ #include "wx/msw/private.h" #endif @@ -105,6 +106,11 @@ 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 +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 '_' + // ---------------------------------------------------------------------------- // global functions // ---------------------------------------------------------------------------- @@ -139,6 +145,23 @@ public: static wxLocale *wxSetLocale(wxLocale *pLocale); +// helper functions of GetSystemLanguage() +#ifdef __UNIX__ + +// get just the language part +static inline wxString ExtractLang(const wxString& langFull) +{ + return langFull.Left(LEN_LANG); +} + +// get everything else (including the leading '_') +static inline wxString ExtractNotLang(const wxString& langFull) +{ + return langFull.Mid(LEN_LANG); +} + +#endif // __UNIX__ + // ---------------------------------------------------------------------------- // wxMsgCatalog corresponds to one disk-file message catalog. // @@ -206,7 +229,7 @@ private: // utility functions // calculate the hash value of given string - static inline size_t32 GetHash(const char *sz); + static size_t32 GetHash(const char *sz); // big<->little endian inline size_t32 Swap(size_t32 ui) const; @@ -436,19 +459,17 @@ const char *wxMsgCatalog::GetString(const char *szOrig) const size_t32 nIncr = 1 + (nHashVal % (m_nHashSize - 2)); -#if defined(__VISAGECPP__) -// VA just can't stand while(1) or while(TRUE) - bool bOs2var = TRUE; - while(bOs2var) { -#else - while (1) { -#endif + for ( ;; ) { size_t32 nStr = Swap(m_pHashTable[nIndex]); if ( nStr == 0 ) return NULL; - if ( strcmp(szOrig, StringAtOfs(m_pOrigTable, nStr - 1)) == 0 ) - return StringAtOfs(m_pTransTable, nStr - 1); + if ( strcmp(szOrig, StringAtOfs(m_pOrigTable, nStr - 1)) == 0 ) { + // work around for BC++ 5.5 bug: without a temp var, the optimizer + // breaks the code and the return value is incorrect + const char *tmp = StringAtOfs(m_pTransTable, nStr - 1); + return tmp; + } if ( nIndex >= m_nHashSize - nIncr) nIndex -= m_nHashSize - nIncr; @@ -467,8 +488,11 @@ const char *wxMsgCatalog::GetString(const char *szOrig) const top = current; else if ( res > 0 ) bottom = current + 1; - else // found! - return StringAtOfs(m_pTransTable, current); + else { // found! + // work around the same BC++ 5.5 bug as above + const char *tmp = StringAtOfs(m_pTransTable, current); + return tmp; + } } } @@ -489,11 +513,16 @@ void wxMsgCatalog::ConvertEncoding() // first, find encoding header: const char *hdr = StringAtOfs(m_pOrigTable, 0); - if (hdr == NULL) return; // not supported by this catalog, does not have non-fuzzy header - if (hdr[0] != 0) return; // ditto + if ( hdr == NULL || hdr[0] != 0 ) { + // not supported by this catalog, does not have non-fuzzy header + return; + } - /* we support catalogs with header (msgid "") that is _not_ marked as "#, fuzzy" (otherwise - the string would not be included into compiled catalog) */ + /* + we support catalogs with header (msgid "") that is _not_ marked as "#, + fuzzy" (otherwise the string would not be included into compiled + catalog) + */ wxString header(StringAtOfs(m_pTransTable, 0)); wxString charset; int pos = header.Find(wxT("Content-Type: text/plain; charset=")); @@ -507,19 +536,35 @@ void wxMsgCatalog::ConvertEncoding() if ( enc == wxFONTENCODING_SYSTEM ) return; // unknown encoding - wxFontEncodingArray a = wxEncodingConverter::GetPlatformEquivalents(enc); - if (a[0] == enc) - return; // no conversion needed, locale uses native encoding + wxFontEncoding targetEnc = wxFONTENCODING_SYSTEM; +#ifdef __UNIX__ + wxString langFull; + if (wxGetEnv(wxT("LC_ALL"), &langFull) || + wxGetEnv(wxT("LC_CTYPE"), &langFull) || + wxGetEnv(wxT("LANG"), &langFull)) + { + wxString lcharset = langFull.AfterFirst(wxT('.')).BeforeFirst(wxT('@')); + if (!lcharset.IsEmpty()) + targetEnc = wxTheFontMapper->CharsetToEncoding(lcharset, FALSE); + } +#endif - if (a.GetCount() == 0) - return; // we don't know common equiv. under this platform + if (targetEnc == wxFONTENCODING_SYSTEM) + { + wxFontEncodingArray a = wxEncodingConverter::GetPlatformEquivalents(enc); + if (a[0] == enc) + return; // no conversion needed, locale uses native encoding + if (a.GetCount() == 0) + return; // we don't know common equiv. under this platform + targetEnc = a[0]; + } wxEncodingConverter converter; + converter.Init(enc, targetEnc); - converter.Init(enc, a[0]); for (size_t i = 0; i < m_numStrings; i++) converter.Convert((char*)StringAtOfs(m_pTransTable, i)); -#endif +#endif // wxUSE_GUI } @@ -531,13 +576,28 @@ void wxMsgCatalog::ConvertEncoding() WX_DECLARE_EXPORTED_OBJARRAY(wxLanguageInfo, wxLanguageInfoArray); WX_DEFINE_OBJARRAY(wxLanguageInfoArray); +wxLanguageInfoArray *wxLocale::ms_languagesDB = NULL; + +/*static*/ void wxLocale::CreateLanguagesDB() +{ + if (ms_languagesDB == NULL) + { + ms_languagesDB = new wxLanguageInfoArray; + InitLanguagesDB(); + } +} + +/*static*/ void wxLocale::DestroyLanguagesDB() +{ + delete ms_languagesDB; + ms_languagesDB = NULL; +} wxLocale::wxLocale() { m_pszOldLocale = NULL; m_pMsgCat = NULL; - m_languagesDB = NULL; m_language = wxLANGUAGE_UNKNOWN; } @@ -583,37 +643,37 @@ bool wxLocale::Init(const wxChar *szName, return bOk; } - - bool wxLocale::Init(int language, int flags) { wxLanguageInfo *info = NULL; int lang = language; - if (m_languagesDB == NULL) + CreateLanguagesDB(); + + if (lang == wxLANGUAGE_DEFAULT) { - m_languagesDB = new wxLanguageInfoArray; - InitLanguagesDB(); + // auto detect the language + lang = GetSystemLanguage(); + } + + // We failed to detect system language, so we will use English: + if (lang == wxLANGUAGE_UNKNOWN) + { + return FALSE; } - if (lang == wxLANGUAGE_DEFAULT) lang = GetSystemLanguage(); - if (lang != wxLANGUAGE_UNKNOWN) + if (lang != wxLANGUAGE_DEFAULT) { - for (size_t i = 0; i < m_languagesDB->GetCount(); i++) + for (size_t i = 0; i < ms_languagesDB->GetCount(); i++) { - if (m_languagesDB->Item(i).Language == lang) + if (ms_languagesDB->Item(i).Language == lang) { - info = &m_languagesDB->Item(i); + info = &ms_languagesDB->Item(i); break; } } } - // We failed to detect system language, so we will use English: - if (lang == wxLANGUAGE_UNKNOWN) - { - return FALSE; - } // Unknown language: if (info == NULL) { @@ -625,11 +685,13 @@ bool wxLocale::Init(int language, int flags) wxString canonical = info->CanonicalName; wxString locale; wxChar *retloc; - + // Set the locale: #ifdef __UNIX__ - if (language == wxLANGUAGE_DEFAULT) locale = wxEmptyString; - else locale = info->CanonicalName; + if (language == wxLANGUAGE_DEFAULT) + locale = wxEmptyString; + else + locale = info->CanonicalName; retloc = wxSetlocale(LC_ALL, locale); @@ -659,7 +721,6 @@ bool wxLocale::Init(int language, int flags) wxLogError(wxT("Cannot set locale to '%s'."), locale.c_str()); return FALSE; } - #elif defined(__WIN32__) if (language != wxLANGUAGE_DEFAULT) { @@ -669,8 +730,8 @@ bool wxLocale::Init(int language, int flags) retloc = wxT("C"); } else - { - wxUint32 lcid = MAKELCID(MAKELANGID(info->WinLang, info->WinSublang), + { + wxUint32 lcid = MAKELCID(MAKELANGID(info->WinLang, info->WinSublang), SORT_DEFAULT); if (SetThreadLocale(lcid)) retloc = wxSetlocale(LC_ALL, wxEmptyString); @@ -707,11 +768,11 @@ bool wxLocale::Init(int language, int flags) wxLogError(wxT("Cannot set locale to language %s."), name.c_str()); return FALSE; } - + #else return FALSE; #endif - + return Init(name, canonical, wxString(retloc), (flags & wxLOCALE_LOAD_DEFAULT) != 0, (flags & wxLOCALE_CONV_ENCODING) != 0); @@ -728,126 +789,163 @@ void wxLocale::AddCatalogLookupPathPrefix(const wxString& prefix) //else: already have it } - -int wxLocale::GetSystemLanguage() const +/*static*/ int wxLocale::GetSystemLanguage() { - int wxlang = wxLANGUAGE_UNKNOWN; - size_t i; + CreateLanguagesDB(); + + // init i to avoid compiler warning + size_t i = 0, + count = ms_languagesDB->GetCount(); - wxASSERT_MSG(m_languagesDB != NULL, "Languages DB not initialized, call wxLocale::Init!"); - #if defined(__UNIX__) - wxString lang; - if (!wxGetEnv(wxT("LC_ALL"), &lang) && - !wxGetEnv(wxT("LC_MESSAGES"), &lang) && - !wxGetEnv(wxT("LANG"), &lang)) - return wxLANGUAGE_UNKNOWN; - - bool is_abbrev = lang.Len() == 2 || - (lang.Len() == 5 && lang[2] == wxT('_')); - - // 0. Make sure the abbrev is according to latest ISO 639 - // (this is neccessary because glibc uses iw and in instead - // of he and id respectively). - if (is_abbrev) + // 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)) { - wxString mid = lang.Mid(0,2); - if (mid == wxT("iw")) lang = wxT("he") + lang.Mid(3); - else if (mid == wxT("in")) lang = wxT("id") + lang.Mid(3); - else if (mid == wxT("ji")) lang = wxT("yi") + lang.Mid(3); + // no language specified, threat it as English + return wxLANGUAGE_ENGLISH; } - // 1. Try to find the lang as is: - if (is_abbrev) + if ( langFull == _T("C") ) { - for (i = 0; i < m_languagesDB->GetCount(); i++) + // default C locale + return wxLANGUAGE_ENGLISH; + } + + // the language string has the following form + // + // lang[_LANG[.encoding]] + // + // where lang is the primary language, LANG is a sublang + // + // for example, the following strings are valid: + // fr + // fr_FR + // de_DE.iso88591 + + // for now we don't use the encoding, although we probably should (doing + // translations of the msg catalogs on the fly as required) (TODO) + langFull = langFull.BeforeFirst(_T('.')); + + // in addition to the format above, we also can have full language names + // in LANG env var - for example, SuSE is known to use LANG="german" - so + // check for this + + // do we have just the language (or sublang too)? + bool justLang = langFull.Len() == LEN_LANG; + if ( justLang || + (langFull.Len() == LEN_FULL && langFull[LEN_LANG] == wxT('_')) ) + { + // 0. Make sure the lang is according to latest ISO 639 + // (this is neccessary 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"); + else + lang = langOrig; + + // did we change it? + if ( lang != langOrig ) + { + langFull = lang + ExtractNotLang(langFull); + } + + // 1. Try to find the language either as is: + for ( i = 0; i < count; i++ ) { - if (m_languagesDB->Item(i).CanonicalName == lang) + if ( ms_languagesDB->Item(i).CanonicalName == langFull ) { - wxlang = m_languagesDB->Item(i).Language; break; } } - } - // 2. If lang is of the form xx_YY, try to find xx: - if (wxlang == wxLANGUAGE_UNKNOWN && is_abbrev && lang.Len() == 5) - { - wxString lang2 = lang.Mid(0,2); - for (i = 0; i < m_languagesDB->GetCount(); i++) + // 2. If langFull is of the form xx_YY, try to find xx: + if ( i == count && !justLang ) { - if (m_languagesDB->Item(i).CanonicalName == lang2) + for ( i = 0; i < count; i++ ) { - wxlang = m_languagesDB->Item(i).Language; - break; + if ( ms_languagesDB->Item(i).CanonicalName == lang ) + { + break; + } } } - } - // 3. If lang is of the form xx, try to find any xx_YY record: - if (wxlang == wxLANGUAGE_UNKNOWN && is_abbrev && lang.Len() == 2) - { - for (i = 0; i < m_languagesDB->GetCount(); i++) + // 3. If langFull is of the form xx, try to find any xx_YY record: + if ( i == count && justLang ) { - if (m_languagesDB->Item(i).CanonicalName.Mid(0,2) == lang) + for ( i = 0; i < count; i++ ) { - wxlang = m_languagesDB->Item(i).Language; - break; + if ( ExtractLang(ms_languagesDB->Item(i).CanonicalName) + == langFull ) + { + break; + } } } } - - // 4. If everything failed, try to find the name in verbose description - // (SuSE is known to use LANG="german"): - if (wxlang == wxLANGUAGE_UNKNOWN && !is_abbrev) + else // not standard format { - for (i = 0; i < m_languagesDB->GetCount(); i++) + // try to find the name in verbose description + for ( i = 0; i < count; i++ ) { - if (m_languagesDB->Item(i).Description.CmpNoCase(lang) == 0) + if (ms_languagesDB->Item(i).Description.CmpNoCase(langFull) == 0) { - wxlang = m_languagesDB->Item(i).Language; break; } } } - #elif defined(__WIN32__) LCID lcid = GetUserDefaultLCID(); - if (lcid == 0) return wxLANGUAGE_UNKNOWN; - wxUint32 lang = PRIMARYLANGID(LANGIDFROMLCID(lcid)); - wxUint32 sublang = SUBLANGID(LANGIDFROMLCID(lcid)); - - for (i = 0; i < m_languagesDB->GetCount(); i++) + if ( lcid != 0 ) { - if (m_languagesDB->Item(i).WinLang == lang && - m_languagesDB->Item(i).WinSublang == sublang) + wxUint32 lang = PRIMARYLANGID(LANGIDFROMLCID(lcid)); + wxUint32 sublang = SUBLANGID(LANGIDFROMLCID(lcid)); + + for ( i = 0; i < count; i++ ) { - wxlang = m_languagesDB->Item(i).Language; - break; + if (ms_languagesDB->Item(i).WinLang == lang && + ms_languagesDB->Item(i).WinSublang == sublang) + { + break; + } } } -#endif - - return wxlang; -} + //else: leave wxlang == wxLANGUAGE_UNKNOWN +#endif // Unix/Win32 + if ( i < count ) + { + // we did find a matching entry, use it + return ms_languagesDB->Item(i).Language; + } + // no info about this language in the database + return wxLANGUAGE_UNKNOWN; +} -void wxLocale::AddLanguage(const wxLanguageInfo& info) +/*static*/ void wxLocale::AddLanguage(const wxLanguageInfo& info) { - wxASSERT_MSG(m_languagesDB != NULL, "Languages DB not initialized, call wxLocale::Init!"); - m_languagesDB->Add(info); + CreateLanguagesDB(); + ms_languagesDB->Add(info); } - - wxString wxLocale::GetSysName() const { return wxSetlocale(LC_ALL, NULL); } - - // clean up wxLocale::~wxLocale() { @@ -859,8 +957,6 @@ wxLocale::~wxLocale() delete pTmpCat; } - delete m_languagesDB; - // restore old locale wxSetLocale(m_pOldLocale); wxSetlocale(LC_ALL, m_pszOldLocale); @@ -966,11 +1062,6 @@ bool wxLocale::AddCatalog(const wxChar *szDomain) } } - - - - - // ---------------------------------------------------------------------------- // global functions and variables // ---------------------------------------------------------------------------- @@ -995,12 +1086,31 @@ wxLocale *wxSetLocale(wxLocale *pLocale) +// ---------------------------------------------------------------------------- +// wxLocale module (for lazy destruction of languagesDB) +// ---------------------------------------------------------------------------- + +class wxLocaleModule: public wxModule +{ + DECLARE_DYNAMIC_CLASS(wxLocaleModule) + public: + wxLocaleModule() {} + bool OnInit() { return TRUE; } + void OnExit() { wxLocale::DestroyLanguagesDB(); } +}; + +IMPLEMENT_DYNAMIC_CLASS(wxLocaleModule, wxModule) + + // ---------------------------------------------------------------------------- // default languages table & initialization // ---------------------------------------------------------------------------- + +// --- --- --- generated code begins here --- --- --- + // This table is generated by misc/languages/genlang.py // When making changes, please put them into misc/languages/langtabl.txt @@ -1738,6 +1848,9 @@ void wxLocale::InitLanguagesDB() }; #undef LNG +// --- --- --- generated code ends here --- --- --- + + #endif // wxUSE_INTL