intl.cpp

来自「A*算法 A*算法 A*算法 A*算法A*算法A*算法」· C++ 代码 · 共 2,270 行 · 第 1/5 页

CPP
2,270
字号
                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;

    // amount of memory pointed to by m_pData.
    size_t32 m_nSize;

    // data description
    size_t32          m_numStrings;   // number of strings in this domain
    wxMsgTableEntry  *m_pOrigTable,   // pointer to original   strings
                     *m_pTransTable;  //            translated

    wxString m_charset;

    // 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;
    }

    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_nSize)
        {
            return NULL;
        }

        return (const char *)(m_pData + ofsString);
    }

    bool m_bSwapped;   // wrong endianness?

    DECLARE_NO_COPY_CLASS(wxMsgCatalogFile)
};


// ----------------------------------------------------------------------------
// wxMsgCatalog corresponds to one loaded message catalog.
//
// This is a "low-level" class and is used only by wxLocale (that's why
// it's designed to be stored in a linked list)
// ----------------------------------------------------------------------------

class wxMsgCatalog
{
public:
    // load the catalog from disk (szDirPrefix corresponds to language)
    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; }

    // get the translated string: returns NULL if not found
    const wxChar *GetString(const wxChar *sz, size_t n = size_t(-1)) const;

    // public variable pointing to the next element in a linked list (or NULL)
    wxMsgCatalog *m_pNext;

private:
    wxMessagesHash  m_messages; // all messages in the catalog
    wxString        m_name;     // name of the domain
    wxPluralFormsCalculatorPtr  m_pluralFormsCalculator;
};

// ----------------------------------------------------------------------------
// global variables
// ----------------------------------------------------------------------------

// the list of the directories to search for message catalog files
static wxArrayString s_searchPrefixes;

// ============================================================================
// implementation
// ============================================================================

// ----------------------------------------------------------------------------
// wxMsgCatalogFile class
// ----------------------------------------------------------------------------

wxMsgCatalogFile::wxMsgCatalogFile()
{
    m_pData = NULL;
    m_nSize = 0;
}

wxMsgCatalogFile::~wxMsgCatalogFile()
{
    wxDELETEA(m_pData);
}

// return all directories to search for given prefix
static wxString GetAllMsgCatalogSubdirs(const wxChar *prefix,
                                        const wxChar *lang)
{
    wxString searchPath;

    // 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;

    return searchPath;
}

// construct the search path for the given language
static wxString GetFullSearchPath(const wxChar *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++ )
    {
        searchPath << GetAllMsgCatalogSubdirs(s_searchPrefixes[n], lang)
                   << wxPATH_SEP;
    }

    // TODO: use wxStandardPaths instead of all this mess!!

    // LC_PATH is a standard env var containing the search path for the .mo
    // files
#ifndef __WXWINCE__
    const wxChar *pszLcPath = wxGetenv(wxT("LC_PATH"));
    if ( pszLcPath != NULL )
        searchPath << GetAllMsgCatalogSubdirs(pszLcPath, lang);
#endif

#ifdef __UNIX__
    // add some standard ones and the one in the tree where wxWin was installed:
    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);
#endif // __UNIX__

    // then take the current directory
    // FIXME it should be the directory of the executable
#if defined(__WXMAC__)
    searchPath << GetAllMsgCatalogSubdirs(wxGetCwd(), lang);
    // generic search paths could be somewhere in the system folder preferences
#elif defined(__WXMSW__)
    // look in the directory of the executable
    wxString path;
    wxSplitPath(wxGetFullModuleName(), &path, NULL, NULL);
    searchPath << GetAllMsgCatalogSubdirs(path, lang);
#else // !Mac, !MSW
    searchPath << GetAllMsgCatalogSubdirs(wxT("."), lang);
#endif // platform

    return searchPath;
}

// open disk file and read in it's contents
bool wxMsgCatalogFile::Load(const wxChar *szDirPrefix, const wxChar *szName,
                            wxPluralFormsCalculatorPtr& rPluralFormsCalculator)
{
  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 << _T('.') << wxFontMapperBase::GetEncodingName(encSys);
    searchPath << GetFullSearchPath(fullname) << wxPATH_SEP;
  }
#endif // wxUSE_FONTMAP


  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;
  }

  // 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, searchPath.c_str());
  wxLogTrace(TRACE_I18N, _T("Looking for \"%s.mo\" in \"%s\""),
             szName, searchPath.c_str());

  wxFileName fn(szName);
  fn.SetExt(_T("mo"));
  wxString strFullName;
  if ( !wxFindFileInPath(&strFullName, searchPath, fn.GetFullPath()) ) {
    wxLogVerbose(_("catalog file for domain '%s' not found."), szName);
    wxLogTrace(TRACE_I18N, _T("Catalog \"%s.mo\" not found"), szName);
    return false;
  }

  // open file
  wxLogVerbose(_("using catalog '%s' from '%s'."), szName, strFullName.c_str());
  wxLogTrace(TRACE_I18N, _T("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 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, (size_t)nSize) != nSize ) {
    wxDELETEA(m_pData);
    return false;
  }

  // examine header
  bool bValid = nSize + (size_t)0 > 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));
  m_nSize = (size_t32)nSize;

  // 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:
      wxString header = wxString::FromAscii(StringAtOfs(m_pTransTable, 0));
      int begin = header.Find(wxT("Content-Type: text/plain; charset="));
      if (begin != wxNOT_FOUND)
      {
          begin += 34; //strlen("Content-Type: text/plain; charset=")
          size_t end = header.find('\n', begin);
          if (end != size_t(-1))
          {
              m_charset.assign(header, begin, end - begin);
              if (m_charset == wxT("CHARSET"))
              {
                  // "CHARSET" is not valid charset, but lazy translator
                  m_charset.Clear();
              }
          }
      }
      // else: incorrectly filled Content-Type header

      // Extract plural forms:
      begin = header.Find(wxT("Plural-Forms:"));
      if (begin != wxNOT_FOUND)
      {
          begin += 13;
          size_t end = header.find('\n', begin);
          if (end != size_t(-1))
          {
              wxString pfs(header, begin, end - begin);
              wxPluralFormsCalculator* pCalculator = wxPluralFormsCalculator
                  ::make(pfs.ToAscii());
              if (pCalculator != 0)
              {
                  rPluralFormsCalculator.reset(pCalculator);
              }
              else
              {
                   wxLogVerbose(_("Cannot parse Plural-Forms:'%s'"), pfs.c_str());
              }
          }
      }
      if (rPluralFormsCalculator.get() == NULL)
      {
          rPluralFormsCalculator.reset(wxPluralFormsCalculator::make());
      }
  }

  // everything is fine
  return true;
}

void wxMsgCatalogFile::FillHash(wxMessagesHash& hash,
                                const wxString& msgIdCharset,
                                bool convertEncoding) const
{
#if wxUSE_FONTMAP
    // determine if we need any conversion at all
    if ( convertEncoding )
    {
        wxFontEncoding encCat = wxFontMapperBase::GetEncodingFromName(m_charset);
        if ( encCat == wxLocale::GetSystemEncoding() )
        {
            // no need to convert
            convertEncoding = false;
        }
    }
#endif // wxUSE_FONTMAP

#if wxUSE_WCHAR_T
    // conversion to use to convert catalog strings to the GUI encoding
    wxMBConv *inputConv,
             *csConv = NULL; // another ptr just to be able to delete it later
    if ( convertEncoding )
    {
        if ( m_charset.empty() )
            inputConv = wxConvCurrent;
        else
            inputConv =
            csConv = new wxCSConv(m_charset);
    }
    else // no need to convert the encoding
    {
        // we still need the conversion for Unicode build
#if wxUSE_UNICODE
        inputConv = wxConvCurrent;
#else // !wxUSE_UNICODE
        inputConv = NULL;
#endif
    }

    // 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 == NULL,
                  _T("non-ASCII msgid languages only supported if wxUSE_WCHAR_T=1") );

    wxEncodingConverter converter;
    if ( convertEncoding )
    {
        wxFontEncoding targetEnc = wxFONTENCODING_SYSTEM;
        wxFontEncoding enc = wxFontMapperBase::Get()->CharsetToEncoding(m_charset, false);
        if ( enc == wxFONTENCODING_SYSTEM )
        {
            convertEncoding = false; // unknown encoding
        }
        else
        {
            targetEnc = wxLocale::GetSystemEncoding();
            if (targetEnc == wxFONTENCODING_SYSTEM)
            {
                wxFontEncodingArray a = wxEncodingConverter::GetPlatformEquivalents(enc);
                if (a[0] == enc)
                    // no conversion needed, locale uses native encoding
                    convertEncoding = false;
                if (a.GetCount() == 0)
                    // we don't know common equiv. under this platform
                    convertEncoding = false;
                targetEnc = a[0];
            }
        }

        if ( convertEncoding )
        {
            converter.Init(enc, targetEnc);
        }
    }
#endif // wxUSE_WCHAR_T/!wxUSE_WCHAR_T
    (void)convertEncoding; // get rid of warnings about unused parameter

    for (size_t32 i = 0; i < m_numStrings; i++)
    {
        const char *data = StringAtOfs(m_pOrigTable, i);
#if wxUSE_UNICODE
        wxString msgid(data, *inputConv);
#else // ASCII
        wxString msgid;
#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);
        size_t length = Swap(m_pTransTable[i].nLen);
        size_t offset = 0;
        size_t index = 0;
        while (offset < length)
        {
            wxString msgstr;
#if wxUSE_WCHAR_T
        #if wxUSE_UNICODE
            msgstr = wxString(data + offset, *inputConv);
        #else
            if ( inputConv )
                msgstr = wxString(inputConv->cMB2WC(data + offset), wxConvLocal);
            else
                msgstr = wxString(data + offset);
        #endif
#else // !wxUSE_WCHAR_T
        #if wxUSE_FONTMAP
            if ( convertEncoding )
                msgstr = wxString(converter.Convert(data + offset));
            else
        #endif
                msgstr = wxString(data + offset);
#endif // wxUSE_WCHAR_T/!wxUSE_WCHAR_T

            if ( !msgstr.empty() )
            {
                hash[index == 0 ? msgid : msgid + wxChar(index)] = msgstr;
            }
            offset += strlen(data + offset) + 1;
            ++index;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?