- public:
- NoTransErr() { wxSuppressTransErrors(); }
- ~NoTransErr() { wxRestoreTransErrors(); }
-};
-
-// open disk file and read in it's contents
-bool wxMsgCatalog::Load(const char *szDirPrefix, const char *szName)
-{
- // search order (assume language 'foo') is
- // 1) $LC_PATH/foo/LC_MESSAGES (if LC_PATH set)
- // 2) ./foo/LC_MESSAGES
- // 3) ./foo
- // 4) . (Added by JACS)
- //
- // under UNIX we search also in:
- // 5) /usr/share/locale/foo/LC_MESSAGES (Linux)
- // 6) /usr/lib/locale/foo/LC_MESSAGES (Solaris)
- #define MSG_PATH FILE_SEP_PATH + "LC_MESSAGES" PATH_SEP
-
- wxString strPath("");
- const char *pszLcPath = getenv("LC_PATH");
- if ( pszLcPath != NULL )
- strPath += pszLcPath + wxString(szDirPrefix) + MSG_PATH; // (1)
-
- // NB: '<<' is unneeded between too literal strings:
- // they are concatenated at compile time
- strPath += "./" + wxString(szDirPrefix) + MSG_PATH // (2)
- + "./" + szDirPrefix + FILE_SEP_PATH + PATH_SEP // (3)
- + "." + PATH_SEP
- #ifdef __UNIX__
- "/usr/share/locale/" + szDirPrefix + MSG_PATH // (5)
- "/usr/lib/locale/" + szDirPrefix + MSG_PATH // (6)
- #endif //UNIX
- ;
-
- 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, strPath.c_str());
-
- wxString strFullName;
- if ( !wxFindFileInPath(&strFullName, strPath, strFile) ) {
- wxLogWarning(_("catalog file for domain '%s' not found."), szName);
- return FALSE;
- }
-
- // open file
- wxLogVerbose(_("using catalog '%s' from '%s'."),
- szName, 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;
- if ( bValid ) {
- pHeader = (wxMsgCatalogHeader *)m_pData;
-
- // 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));
-
- m_nHashSize = Swap(pHeader->nHashSize);
- m_pHashTable = (size_t32 *)(m_pData + Swap(pHeader->ofsHashTable));
-
- m_pszName = new char[strlen(szName) + 1];
- strcpy(m_pszName, szName);
-
- // everything is fine
- return TRUE;
-}
-
-// search for a string
-const char *wxMsgCatalog::GetString(const char *szOrig) const
-{
- if ( szOrig == NULL )
- return NULL;
+#if WXWIN_COMPATIBILITY_2_8
+ wxASSERT_MSG( !(flags & wxLOCALE_CONV_ENCODING),
+ wxS("wxLOCALE_CONV_ENCODING is no longer supported, add charset to your catalogs") );
+#endif
+
+ bool ret = true;
+
+ int lang = language;
+ if (lang == wxLANGUAGE_DEFAULT)
+ {
+ // auto detect the language
+ lang = GetSystemLanguage();
+ }
+
+ // We failed to detect system language, so we will use English:
+ if (lang == wxLANGUAGE_UNKNOWN)
+ {
+ return false;
+ }
+
+ const wxLanguageInfo *info = GetLanguageInfo(lang);
+
+ // Unknown language:
+ if (info == NULL)
+ {
+ wxLogError(wxS("Unknown language %i."), lang);
+ return false;
+ }
+
+ wxString name = info->Description;
+ wxString canonical = info->CanonicalName;
+ wxString locale;
+
+ // Set the locale:
+#if defined(__OS2__)
+ const char *retloc = wxSetlocale(LC_ALL , wxEmptyString);
+#elif defined(__UNIX__) && !defined(__WXMAC__)
+ if (language != wxLANGUAGE_DEFAULT)
+ locale = info->CanonicalName;
+
+ const char *retloc = wxSetlocaleTryUTF8(LC_ALL, locale);
+
+ const wxString langOnly = ExtractLang(locale);
+ if ( !retloc )
+ {
+ // Some C libraries don't like xx_YY form and require xx only
+ retloc = wxSetlocaleTryUTF8(LC_ALL, langOnly);
+ }
+
+#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 )
+ {
+ const wxChar **names =
+ wxFontMapperBase::GetAllEncodingNames(wxFONTENCODING_UTF8);
+ while ( *names )
+ {
+ retloc = wxSetlocale(LC_ALL, locale + wxS('.') + *names++);
+ if ( retloc )
+ break;
+ }
+ }
+#endif // wxUSE_FONTMAP
+
+ if ( !retloc )
+ {
+ // 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<char*>(wxStrchr(retloc, ' '));
+ if ( p )
+ *p = '\0';
+#endif // __AIX__
+
+#elif defined(__WIN32__)
+ const char *retloc = "C";
+ if ( language != wxLANGUAGE_DEFAULT )
+ {
+ if ( info->WinLang == 0 )
+ {
+ wxLogWarning(wxS("Locale '%s' not supported by OS."), name.c_str());
+ // retloc already set to "C"
+ }
+ else // language supported by Windows
+ {
+ // 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() )
+ {
+ ret = false;
+ }
+ else // have a valid locale
+ {
+ retloc = wxSetlocale(LC_ALL, locale);
+ }
+ }
+ }
+ else // language == wxLANGUAGE_DEFAULT
+ {
+ retloc = wxSetlocale(LC_ALL, wxEmptyString);
+ }
+
+#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 )
+ {
+ 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__)
+ 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
+ wxUnusedVar(flags);
+ return false;
+ #define WX_NO_LOCALE_SUPPORT
+#endif
+
+#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 ( !DoInit(name, canonical, retloc) )
+ {
+ ret = false;
+ }
+
+ if (IsOk()) // setlocale() succeeded
+ m_language = lang;
+
+ // NB: don't use 'lang' here, 'language'
+ wxTranslations *t = wxTranslations::Get();
+ if ( t )
+ {
+ t->SetLanguage(static_cast<wxLanguage>(language));
+
+ if ( flags & wxLOCALE_LOAD_DEFAULT )
+ t->AddStdCatalog();
+ }
+
+ return ret;
+#endif // !WX_NO_LOCALE_SUPPORT
+}
+
+namespace
+{
+
+// Small helper function: get the value of the given environment variable and
+// return true only if the variable was found and has non-empty value.
+inline bool wxGetNonEmptyEnvVar(const wxString& name, wxString* value)
+{
+ return wxGetEnv(name, value) && !value->empty();
+}
+
+} // anonymous namespace
+
+/*static*/ int wxLocale::GetSystemLanguage()
+{
+ CreateLanguagesDB();
+
+ // init i to avoid compiler warning
+ size_t i = 0,
+ count = ms_languagesDB->GetCount();
+
+#if defined(__UNIX__)
+ // first get the string identifying the language from the environment
+ wxString langFull;
+#ifdef __WXMAC__
+ wxCFRef<CFLocaleRef> 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 (!wxGetNonEmptyEnvVar(wxS("LC_ALL"), &langFull) &&
+ !wxGetNonEmptyEnvVar(wxS("LC_MESSAGES"), &langFull) &&
+ !wxGetNonEmptyEnvVar(wxS("LANG"), &langFull))
+ {
+ // no language specified, treat it as English
+ return wxLANGUAGE_ENGLISH_US;
+ }
+
+ if ( langFull == wxS("C") || langFull == wxS("POSIX") )
+ {
+ // default C locale is English too
+ return wxLANGUAGE_ENGLISH_US;
+ }
+#endif
+
+ // the language string has the following form
+ //
+ // lang[_LANG][.encoding][@modifier]
+ //
+ // (see environ(5) in the Open Unix specification)
+ //
+ // where lang is the primary language, LANG is a sublang/territory,
+ // encoding is the charset to use and modifier "allows the user to select
+ // a specific instance of localization data within a single category"
+ //
+ // for example, the following strings are valid:
+ // fr
+ // fr_FR
+ // de_DE.iso88591
+ // de_DE@euro
+ // de_DE.iso88591@euro
+
+ // 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 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);
+ }
+
+ // do we have just the language (or sublang too)?
+ const bool justLang = langFull.find('_') == wxString::npos;
+
+ // 0. Make sure the lang is according to latest ISO 639
+ // (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 == 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;
+
+ // did we change it?
+ if ( lang != langOrig )
+ {
+ langFull = lang + ExtractNotLang(langFull);
+ }
+
+ // 1. Try to find the language either as is:
+ // a) With modifier if set
+ if ( !modifier.empty() )
+ {
+ wxString langFullWithModifier = langFull + modifier;
+ for ( i = 0; i < count; i++ )
+ {
+ 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;
+ }
+ }
+
+ // 2. If langFull is of the form xx_YY, try to find xx:
+ if ( i == count && !justLang )
+ {
+ for ( i = 0; i < count; i++ )
+ {
+ if ( ms_languagesDB->Item(i).CanonicalName == lang )
+ {
+ break;
+ }
+ }
+ }
+
+ // 3. If langFull is of the form xx, try to find any xx_YY record:
+ if ( i == count && justLang )
+ {
+ for ( i = 0; i < count; i++ )
+ {
+ if ( ExtractLang(ms_languagesDB->Item(i).CanonicalName)
+ == langFull )
+ {
+ break;
+ }
+ }
+ }
+
+
+ if ( i == count )
+ {
+ // 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 use of non-standard format and try to
+ // find the name in verbose description.
+ for ( i = 0; i < count; i++ )
+ {
+ if (ms_languagesDB->Item(i).Description.CmpNoCase(langFull) == 0)
+ {
+ break;
+ }
+ }
+ }
+#elif defined(__WIN32__)
+ LCID lcid = GetUserDefaultLCID();
+ if ( lcid != 0 )
+ {
+ wxUint32 lang = PRIMARYLANGID(LANGIDFROMLCID(lcid));
+ wxUint32 sublang = SUBLANGID(LANGIDFROMLCID(lcid));
+
+ for ( i = 0; i < count; i++ )
+ {
+ if (ms_languagesDB->Item(i).WinLang == lang &&
+ ms_languagesDB->Item(i).WinSublang == sublang)
+ {
+ break;
+ }
+ }
+ }
+ //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;
+ }