diff options
Diffstat (limited to 'common/translation.cpp')
-rw-r--r-- | common/translation.cpp | 206 |
1 files changed, 184 insertions, 22 deletions
diff --git a/common/translation.cpp b/common/translation.cpp index 36ce4be83f..3bb2260e14 100644 --- a/common/translation.cpp +++ b/common/translation.cpp @@ -29,6 +29,8 @@ #undef ARRAYSIZE #endif +#define TRANSLATIONS_DAT_VER 1 + #include "translation.h" DECLARE_SINGLETON(Common::TranslationManager) @@ -39,10 +41,6 @@ DECLARE_SINGLETON(Common::TranslationManager) #endif // !WIN32 #endif -#ifdef USE_TRANSLATION -#include "messages.cpp" -#endif - namespace Common { @@ -50,7 +48,9 @@ namespace Common { // Translation enabled -TranslationManager::TranslationManager() { +TranslationManager::TranslationManager() : _currentLang(-1) { + loadTranslationsInfoDat(); + #ifdef USE_DETECTLANG #ifdef WIN32 // We can not use "setlocale" (at least not for MSVC builds), since it @@ -120,30 +120,73 @@ TranslationManager::~TranslationManager() { } void TranslationManager::setLanguage(const char *lang) { - if (*lang == '\0') - po2c_setlang(_syslang.c_str()); - else - po2c_setlang(lang); + // Get lang index + int langIndex = -1; + String langStr(lang); + if (langStr.empty()) + langStr = _syslang; + + // Searching for a valid language + for (unsigned int i = 0; i < _langs.size() && langIndex == -1; ++i) { + if (langStr == _langs[i]) + langIndex = i; + } + + // Try partial match + for (unsigned int i = 0; i < _langs.size() && langIndex == -1; ++i) { + if (strncmp(langStr.c_str(), _langs[i].c_str(), 2) == 0) + langIndex = i; + } + + // Load messages for that lang + // Call it even if the index is -1 to unload previously loaded translations + if (langIndex != _currentLang) { + loadLanguageDat(langIndex); + _currentLang = langIndex; + } } const char *TranslationManager::getTranslation(const char *message) { - return po2c_gettext(message); + // if no language is set or message is empty, return msgid as is + if (_currentTranslationMessages.empty() || *message == '\0') + return message; + + // binary-search for the msgid + int leftIndex = 0; + int rightIndex = _currentTranslationMessages.size() - 1; + + while (rightIndex >= leftIndex) { + const int midIndex = (leftIndex + rightIndex) / 2; + const PoMessageEntry * const m = &_currentTranslationMessages[midIndex]; + + const int compareResult = strcmp(message, _messageIds[m->msgid].c_str()); + + if (compareResult == 0) + return m->msgstr.c_str(); + else if (compareResult < 0) + rightIndex = midIndex - 1; + else + leftIndex = midIndex + 1; + } + + return message; } const char *TranslationManager::getCurrentCharset() { - return po2c_getcharset(); + if (_currentCharset.empty()) + return "ASCII"; + return _currentCharset.c_str(); } String TranslationManager::getTranslation(const String &message) { - return po2c_gettext(message.c_str()); + return getTranslation(message.c_str()); } const TLangArray TranslationManager::getSupportedLanguageNames() const { TLangArray languages; - int total = po2c_getnumlangs(); - for (int i = 0; i < total; i++) { - TLanguage lng(po2c_getlangname(i), i + 1); + for (unsigned int i = 0; i < _langNames.size(); i++) { + TLanguage lng(_langNames[i].c_str(), i + 1); languages.push_back(lng); } @@ -153,17 +196,14 @@ const TLangArray TranslationManager::getSupportedLanguageNames() const { } int TranslationManager::parseLanguage(const String lang) { - int total = po2c_getnumlangs(); - - for (int i = 0; i < total; i++) { - if (lang == po2c_getlang(i)) + for (unsigned int i = 0; i < _langs.size(); i++) { + if (lang == _langs[i]) return i + 1; } return kTranslationBuiltinId; } - const char *TranslationManager::getLangById(int id) { switch (id) { case kTranslationAutodetectId: @@ -171,8 +211,8 @@ const char *TranslationManager::getLangById(int id) { case kTranslationBuiltinId: return "C"; default: - if (id >= 0 && id - 1 < po2c_getnumlangs()) - return po2c_getlang(id - 1); + if (id >= 0 && id - 1 < (int)_langs.size()) + return _langs[id - 1].c_str(); } // In case an invalid ID was specified, we will output a warning @@ -181,6 +221,128 @@ const char *TranslationManager::getLangById(int id) { return ""; } +void TranslationManager::loadTranslationsInfoDat() { + File in; + in.open("translations.dat"); + + if (!checkHeader(in)) + return; + + char buf[256]; + int len; + + // Get number of translations + int nbTranslations = in.readUint16BE(); + + // Skip all the block sizes + for (int i = 0 ; i < nbTranslations + 2 ; ++i) + in.readUint16BE(); + + // Read list of languages + _langs.resize(nbTranslations); + _langNames.resize(nbTranslations); + for (int i = 0; i < nbTranslations; ++i) { + len = in.readUint16BE(); + in.read(buf, len); + _langs[i] = String(buf, len); + len = in.readUint16BE(); + in.read(buf, len); + _langNames[i] = String(buf, len); + } + + // Read messages + int numMessages = in.readUint16BE(); + _messageIds.resize(numMessages); + for (int i = 0; i < numMessages; ++i) { + len = in.readUint16BE(); + in.read(buf, len); + _messageIds[i] = String(buf, len); + } +} + +void TranslationManager::loadLanguageDat(int index) { + _currentTranslationMessages.clear(); + _currentCharset.clear(); + // Sanity check + if (index < 0 || index >= (int)_langs.size()) { + if (index != -1) + warning("Invalid language index %d passed to TranslationManager::loadLanguageDat", index); + return; + } + + File in; + in.open("translations.dat"); + + if (!checkHeader(in)) + return; + + char buf[1024]; + int len; + + // Get number of translations + int nbTranslations = in.readUint16BE(); + if (nbTranslations != (int)_langs.size()) { + warning("The 'translations.dat' file has changed since starting ScummVM. GUI translation will not be available"); + return; + } + + // Get size of blocks to skip. + int skipSize = 0; + for (int i = 0 ; i < index + 2 ; ++i) + skipSize += in.readUint16BE(); + // We also need to skip the remaining block sizes + skipSize += 2 * (nbTranslations - index); + + // Seek to start of block we want to read + in.seek(skipSize, SEEK_CUR); + + // Read number of translated messages + int nbMessages = in.readUint16BE(); + _currentTranslationMessages.resize(nbMessages); + + // Read charset + len = in.readUint16BE(); + in.read(buf, len); + _currentCharset = String(buf, len); + + // Read messages + for (int i = 0; i < nbMessages; ++i) { + _currentTranslationMessages[i].msgid = in.readUint16BE(); + len = in.readUint16BE(); + in.read(buf, len); + _currentTranslationMessages[i].msgstr = String(buf, len); + } +} + +bool TranslationManager::checkHeader(File& in) { + char buf[13]; + int ver; + + if (!in.isOpen()) { + warning("You're missing the 'translations.dat' file. GUI translation will not be available"); + return false; + } + + in.read(buf, 12); + buf[12] = '\0'; + + // Check header + if (strcmp(buf, "TRANSLATIONS")) { + warning("File 'translations.dat' is corrupt. GUI translation will not be available"); + return false; + } + + // Check version + ver = in.readByte(); + + if (ver != TRANSLATIONS_DAT_VER) { + warning("File 'translations.dat' is wrong version. Expected %d but got %d. GUI translation will not be available", TRANSLATIONS_DAT_VER, ver); + return false; + } + + return true; +} + #else // USE_TRANSLATION // Translation disabled |