/////////////////////////////////////////////////////////////////////////////
// Name: src/common/intl.cpp
-// Purpose: Internationalization and localisation for wxWindows
+// Purpose: Internationalization and localisation for wxWidgets
// Author: Vadim Zeitlin
// Modified by: Michael N. Filippov <michael@idisys.iae.nsk.su>
// (2003/09/30 - PluralForms support)
#pragma implementation "intl.h"
#endif
+#if defined(__BORLAND__) && !defined(__WXDEBUG__)
+ // There's a bug in Borland's compiler that breaks wxLocale with -O2,
+ // so make sure that flag is not used for this file:
+ #pragma option -O1
+#endif
+
+#ifdef __EMX__
+// The following define is needed by Innotek's libc to
+// make the definition of struct localeconv available.
+#define __INTERNAL_DEFS
+#endif
+
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#include <langinfo.h>
#endif
-// wxWindows
+// wxWidgets
#ifndef WX_PRECOMP
#include "wx/string.h"
#include "wx/intl.h"
// Plural forms parser
// ----------------------------------------------------------------------------
-/*
+/*
Simplified Grammar
Expression:
MultiplicativeExpression ">=" MultiplicativeExpression
MultiplicativeExpression "<=" MultiplicativeExpression
MultiplicativeExpression
-
+
MultiplicativeExpression:
PmExpression '%' PmExpression
PmExpression
wxPluralFormsToken m_token;
wxPluralFormsNodePtr m_nodes[3];
};
-
+
wxPluralFormsNodePtr::~wxPluralFormsNodePtr()
{
delete m_p;
{
public:
wxPluralFormsCalculator() : m_nplurals(0), m_plural(0) {}
-
+
// input: number, returns msgstr index
int evaluate(int n) const;
void init(wxPluralFormsToken::Number nplurals, wxPluralFormsNode* plural);
wxString getString() const;
-
+
private:
wxPluralFormsToken::Number m_nplurals;
wxPluralFormsNodePtr m_plural;
wxPluralFormsScanner& m_scanner;
const wxPluralFormsToken& token() const;
bool nextToken();
-
+
wxPluralFormsNode* expression();
wxPluralFormsNode* logicalOrExpression();
wxPluralFormsNode* logicalAndExpression();
wxPluralFormsCalculatorPtr& rPluralFormsCalculator);
// fills the hash with string-translation pairs
- void FillHash(wxMessagesHash& hash, bool convertEncoding) const;
+ void FillHash(wxMessagesHash& hash, const wxString& msgIdCharset,
+ bool convertEncoding) const;
private:
// this implementation is binary compatible with GNU gettext() version 0.10
*m_pTransTable; // translated
wxString m_charset;
-
+
// swap the 2 halves of 32 bit integer if needed
size_t32 Swap(size_t32 ui) const
{
}
return (const char *)(m_pData + ofsString);
- }
+ }
bool m_bSwapped; // wrong endianness?
{
public:
// load the catalog from disk (szDirPrefix corresponds to language)
- bool Load(const wxChar *szDirPrefix, const wxChar *szName, bool bConvertEncoding = FALSE);
+ bool Load(const wxChar *szDirPrefix, const wxChar *szName,
+ const wxChar *msgIdCharset = NULL, bool bConvertEncoding = false);
// get name of the catalog
wxString GetName() const { return m_name; }
m_pTransTable = (wxMsgTableEntry *)(m_pData +
Swap(pHeader->ofsTransTable));
m_nSize = nSize;
-
+
// now parse catalog's header and try to extract catalog charset and
// plural forms formula from it:
}
}
// else: incorrectly filled Content-Type header
-
+
// Extract plural forms:
begin = header.Find(wxT("Plural-Forms:"));
if (begin != wxNOT_FOUND)
return true;
}
-void wxMsgCatalogFile::FillHash(wxMessagesHash& hash, bool convertEncoding) const
+void wxMsgCatalogFile::FillHash(wxMessagesHash& hash,
+ const wxString& msgIdCharset,
+ bool convertEncoding) const
{
#if wxUSE_WCHAR_T
wxCSConv *csConv = NULL;
csConv = new wxCSConv(m_charset);
wxMBConv& inputConv = csConv ? *((wxMBConv*)csConv) : *wxConvCurrent;
+
+ wxCSConv *sourceConv = NULL;
+ if ( !msgIdCharset.empty() && (m_charset != msgIdCharset) )
+ sourceConv = new wxCSConv(msgIdCharset);
+
#elif wxUSE_FONTMAP
+ wxASSERT_MSG( msgIdCharset == NULL,
+ _T("non-ASCII msgid languages only supported if wxUSE_WCHAR_T=1") );
+
wxEncodingConverter converter;
if ( convertEncoding )
{
for (size_t i = 0; i < m_numStrings; i++)
{
const char *data = StringAtOfs(m_pOrigTable, i);
-#if wxUSE_WCHAR_T
+#if wxUSE_UNICODE
wxString msgid(data, inputConv);
#else
- wxString msgid(data);
+ wxString msgid;
+#if wxUSE_WCHAR_T
+ if ( convertEncoding && sourceConv )
+ msgid = wxString(inputConv.cMB2WC(data), *sourceConv);
+ else
#endif
+ msgid = data;
+#endif // wxUSE_UNICODE
data = StringAtOfs(m_pTransTable, i);
size_t length = Swap(m_pTransTable[i].nLen);
}
#if wxUSE_WCHAR_T
+ delete sourceConv;
delete csConv;
#endif
}
// ----------------------------------------------------------------------------
bool wxMsgCatalog::Load(const wxChar *szDirPrefix, const wxChar *szName,
- bool bConvertEncoding)
+ const wxChar *msgIdCharset, bool bConvertEncoding)
{
wxMsgCatalogFile file;
if ( file.Load(szDirPrefix, szName, m_pluralFormsCalculator) )
{
- file.FillHash(m_messages, bConvertEncoding);
+ file.FillHash(m_messages, msgIdCharset, bConvertEncoding);
return TRUE;
}
}
-wxLocale::wxLocale()
+void wxLocale::DoCommonInit()
{
m_pszOldLocale = NULL;
m_pMsgCat = NULL;
{
wxASSERT_MSG( !m_initialized,
_T("you can't call wxLocale::Init more than once") );
-
+
m_initialized = true;
m_strLocale = szName;
m_strShort = szShort;
256);
if (ret != 0)
{
- m_pszOldLocale = wxStrdup(localeName);
+ m_pszOldLocale = wxStrdup(localeName);
}
else
m_pszOldLocale = NULL;
// TODO: how to find languageId
// SetLocaleInfo(languageId, SORT_DEFAULT, localeName);
#else
- m_pszOldLocale = wxSetlocale(LC_ALL, szLocale);
- if ( m_pszOldLocale )
- m_pszOldLocale = wxStrdup(m_pszOldLocale);
+ wxMB2WXbuf oldLocale = wxSetlocale(LC_ALL, szLocale);
+ if ( oldLocale )
+ m_pszOldLocale = wxStrdup(oldLocale);
+ else
+ m_pszOldLocale = NULL;
#endif
if ( m_pszOldLocale == NULL )
// save the old locale to be able to restore it later
m_pOldLocale = wxSetLocale(this);
- // load the default catalog with wxWindows standard messages
+ // load the default catalog with wxWidgets standard messages
m_pMsgCat = NULL;
bool bOk = TRUE;
if ( bLoadDefault )
// #ifdef SETLOCALE_FAILS_ON_UNICODE_LANGS bellow.
#define SETLOCALE_FAILS_ON_UNICODE_LANGS
#endif
-
+
+#if !wxUSE_UNICODE
+ const
+#endif
wxMB2WXbuf retloc = wxT("C");
if (language != wxLANGUAGE_DEFAULT)
{
}
else
{
- int codepage
+ int codepage
#ifdef SETLOCALE_FAILS_ON_UNICODE_LANGS
= -1
#endif
// 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()
// 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 )
+ //
+ // HP-UX uses HP-Roman8 cset which is not the same as ASCII (see RFC
+ // 1345 for its definition) but must be recognized as otherwise HP
+ // users get a warning about it on each program startup, so handle it
+ // here -- but it would be obviously better to add real supprot to it,
+ // of course!
+ if ( strcmp(alang, "646") == 0
+ || strcmp(alang, "ANSI_X3.4-1968") == 0
+#ifdef __HPUX__
+ || strcmp(alang, "roman8") == 0
+#endif // __HPUX__
+ )
{
encname = _T("US-ASCII");
}
#if defined(__WIN32__) && !defined(__WXMICROWIN__)
UINT codepage = ::GetACP();
- // wxWindows only knows about CP1250-1257, 932, 936, 949, 950
+ // wxWidgets only knows about CP1250-1257, 932, 936, 949, 950
if ( codepage >= 1250 && codepage <= 1257 )
{
return (wxFontEncoding)(wxFONTENCODING_CP1250 + codepage - 1250);
return wxFONTENCODING_CP950;
}
#elif defined(__WXMAC__)
- return wxMacGetFontEncFromSystemEnc( CFStringGetSystemEncoding() ) ;
+ TextEncoding encoding = 0 ;
+#if TARGET_CARBON
+ encoding = CFStringGetSystemEncoding() ;
+#else
+ UpgradeScriptInfoToTextEncoding ( smSystemScript , kTextLanguageDontCare , kTextRegionDontCare , NULL , &encoding ) ;
+#endif
+ return wxMacGetFontEncFromSystemEnc( encoding ) ;
#elif defined(__UNIX_LIKE__) && wxUSE_FONTMAP
wxString encname = GetSystemEncodingName();
if ( !encname.empty() )
{
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 ( szDomain != NULL )
{
wxLogTrace(_T("i18n"),
- _T("string '%s'[%d] not found in domain '%s' for locale '%s'."),
- szOrigString, n, szDomain, m_strLocale.c_str());
+ _T("string '%s'[%lu] not found in domain '%s' for locale '%s'."),
+ szOrigString, (unsigned long)n,
+ szDomain, m_strLocale.c_str());
}
else
{
wxLogTrace(_T("i18n"),
- _T("string '%s'[%d] not found in locale '%s'."),
- szOrigString, n, m_strLocale.c_str());
+ _T("string '%s'[%lu] not found in locale '%s'."),
+ szOrigString, (unsigned long)n, m_strLocale.c_str());
}
}
#endif // __WXDEBUG__
return pszTrans;
}
+wxString wxLocale::GetHeaderValue( const wxChar* szHeader,
+ const wxChar* szDomain ) const
+{
+ if ( wxIsEmpty(szHeader) )
+ return wxEmptyString;
+
+ wxChar const * pszTrans = NULL;
+ wxMsgCatalog *pMsgCat;
+
+ if ( szDomain != NULL )
+ {
+ pMsgCat = FindCatalog(szDomain);
+
+ // does the catalog exist?
+ if ( pMsgCat == NULL )
+ return wxEmptyString;
+
+ pszTrans = pMsgCat->GetString(wxT(""), (size_t)-1);
+ }
+ else
+ {
+ // search in all domains
+ for ( pMsgCat = m_pMsgCat; pMsgCat != NULL; pMsgCat = pMsgCat->m_pNext )
+ {
+ pszTrans = pMsgCat->GetString(wxT(""), (size_t)-1);
+ if ( pszTrans != NULL ) // take the first found
+ break;
+ }
+ }
+
+ if ( wxIsEmpty(pszTrans) )
+ return wxEmptyString;
+
+ wxChar const * pszFound = wxStrstr(pszTrans, szHeader);
+ if ( pszFound == NULL )
+ return wxEmptyString;
+
+ pszFound += wxStrlen(szHeader) + 2 /* ': ' */;
+
+ // Every header is separated by \n
+
+ wxChar const * pszEndLine = wxStrchr(pszFound, wxT('\n'));
+ if ( pszEndLine == NULL ) pszEndLine = pszFound + wxStrlen(pszFound);
+
+
+ // wxString( wxChar*, length);
+ wxString retVal( pszFound, pszEndLine - pszFound );
+
+ return retVal;
+}
+
+
// find catalog by name in a linked list, return NULL if !found
wxMsgCatalog *wxLocale::FindCatalog(const wxChar *szDomain) const
{
// add a catalog to our linked list
bool wxLocale::AddCatalog(const wxChar *szDomain)
+{
+ return AddCatalog(szDomain, wxLANGUAGE_ENGLISH, NULL);
+}
+
+// add a catalog to our linked list
+bool wxLocale::AddCatalog(const wxChar *szDomain,
+ wxLanguage msgIdLanguage,
+ const wxChar *msgIdCharset)
+
{
wxMsgCatalog *pMsgCat = new wxMsgCatalog;
- if ( pMsgCat->Load(m_strShort, szDomain, m_bConvertEncoding) ) {
+ 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;
- // it's OK to not load English catalog, the texts are embedded in
- // the program:
- if (m_strShort.Mid(0, 2) == wxT("en"))
- return TRUE;
+ // 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 (m_language == msgIdLanguage)
+ return true;
- 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 &&
+ msgIdLangInfo->CanonicalName.Mid(0, 2) == m_strShort.Mid(0, 2) )
+ {
+ return true;
+ }
+
+ return false;
}
}
// accessors for locale-dependent data
// ----------------------------------------------------------------------------
-#if 0
-
#ifdef __WXMSW__
/* static */
-wxString wxLocale::GetInfo(wxLocaleInfo index)
+wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory WXUNUSED(cat))
{
wxString str;
wxChar buffer[256];
buffer[0] = wxT('\0');
switch (index)
{
- case wxSYS_DECIMAL_SEPARATOR:
+ case wxLOCALE_DECIMAL_POINT:
count = ::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, buffer, 256);
if (!count)
- str << ".";
+ str << wxT(".");
else
str << buffer;
break;
+#if 0
case wxSYS_LIST_SEPARATOR:
count = ::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SLIST, buffer, 256);
if (!count)
- str << ",";
+ str << wxT(",");
else
str << buffer;
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";
+ str << wxT("0");
else
str << buffer;
break;
+#endif
default:
- wxFAIL_MSG("Unknown System String !");
+ wxFAIL_MSG(wxT("Unknown System String !"));
}
return str;
}
#else // !__WXMSW__
/* static */
-wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory)
+wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory cat)
{
- return wxEmptyString;
+ struct lconv *locale_info = localeconv();
+ switch (cat)
+ {
+ case wxLOCALE_CAT_NUMBER:
+ switch (index)
+ {
+ case wxLOCALE_THOUSANDS_SEP:
+ return wxString(locale_info->thousands_sep,
+ *wxConvCurrent);
+ case wxLOCALE_DECIMAL_POINT:
+ return wxString(locale_info->decimal_point,
+ *wxConvCurrent);
+ default:
+ return wxEmptyString;
+ }
+ case wxLOCALE_CAT_MONEY:
+ switch (index)
+ {
+ case wxLOCALE_THOUSANDS_SEP:
+ return wxString(locale_info->mon_thousands_sep,
+ *wxConvCurrent);
+ case wxLOCALE_DECIMAL_POINT:
+ return wxString(locale_info->mon_decimal_point,
+ *wxConvCurrent);
+ default:
+ return wxEmptyString;
+ }
+ default:
+ return wxEmptyString;
+ }
}
#endif // __WXMSW__/!__WXMSW__
-#endif // 0
-
// ----------------------------------------------------------------------------
// global functions and variables
// ----------------------------------------------------------------------------
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, "zh_TW", LANG_CHINESE , SUBLANG_DEFAULT , "Chinese")
LNG(wxLANGUAGE_CHINESE_SIMPLIFIED, "zh_CN", LANG_CHINESE , SUBLANG_CHINESE_SIMPLIFIED , "Chinese (Simplified)")
LNG(wxLANGUAGE_CHINESE_TRADITIONAL, "zh_TW", LANG_CHINESE , SUBLANG_CHINESE_TRADITIONAL , "Chinese (Traditional)")
LNG(wxLANGUAGE_CHINESE_HONGKONG, "zh_HK", LANG_CHINESE , SUBLANG_CHINESE_HONGKONG , "Chinese (Hongkong)")