aboutsummaryrefslogtreecommitdiff
path: root/common/translation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'common/translation.cpp')
-rw-r--r--common/translation.cpp206
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