// // SuperTuxKart - a fun racing game with go-kart // Copyright (C) 2006, 2007, 2008 Joerg Henrichs // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 3 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // Note: the irrlicht include is only here (and esp. before including // translation.hpp, which contradicts our style rule) to avoid the // warning message " 'swprintf' : macro redefinition" // This happens if libintl.h is included before irrlicht.h (since // both files redefine swprintf). #include "utils/translation.hpp" #include #include #include #include #include #include #include #include #if ENABLE_BIDI # include #endif #include "io/file_manager.hpp" #include "utils/constants.hpp" #include "utils/log.hpp" #include "utils/utf8.h" // set to 1 to debug i18n #define TRANSLATE_VERBOSE 0 using namespace tinygettext; Translations* translations = NULL; const bool REMOVE_BOM = false; #ifdef LINUX // m_debug #define PACKAGE "supertuxkart" #endif /** The list of available languages; this is global so that it is cached (and remains even if the translations object is deleted and re-created) */ std::vector g_language_list; // Note : this method is not static because 'g_language_list' is initialized // the first time Translations is constructed (despite being a global) const std::vector* Translations::getLanguageList() const { return &g_language_list; } char* wide_to_utf8(const wchar_t* input) { static std::vector utf8line; utf8line.clear(); utf8::utf16to8(input, input + wcslen(input), back_inserter(utf8line)); utf8line.push_back(0); return &utf8line[0]; } wchar_t* utf8_to_wide(const char* input) { static std::vector utf16line; utf16line.clear(); utf8::utf8to16(input, input + strlen(input), back_inserter(utf16line)); utf16line.push_back(0); return &utf16line[0]; } // ---------------------------------------------------------------------------- Translations::Translations() //: m_dictionary_manager("UTF-16") { if (g_language_list.size() == 0) { std::set flist; file_manager->listFiles(flist, file_manager->getTranslationDir(), /*is full path*/true); // English is always there but won't be found on file system g_language_list.push_back("en"); std::set::iterator it; for ( it=flist.begin() ; it != flist.end(); it++ ) { if (StringUtils::hasSuffix(*it, "po")) { g_language_list.push_back (m_dictionary_manager.convertFilename2Language(*it) ); // printf("Lang : <%s>\n", (*it).c_str()); } } // for it in flist } // if (g_language_list.size() == 0) // LC_ALL does not work, sscanf will then not always be able // to scan for example: s=-1.1,-2.3,-3.3 correctly, which is // used in driveline files. #if defined(WIN32) && !defined(__CYGWIN__) // Windows does not have LC_MESSAGES setlocale(LC_CTYPE, ""); #else setlocale(LC_MESSAGES, ""); #endif /* bindtextdomain (PACKAGE, file_manager->getTranslationDir().c_str()); if (sizeof(wchar_t) == 4) { if (IS_LITTLE_ENDIAN) bind_textdomain_codeset(PACKAGE, "UTF-32LE"); else bind_textdomain_codeset(PACKAGE, "UTF-32BE"); } else if (sizeof(wchar_t) == 2) { bind_textdomain_codeset(PACKAGE, "UTF-16LE"); } else { fprintf(stderr, "Your wchar_t is neither 2 byte-long nor 4. What now??\n"); exit(1); } textdomain (PACKAGE); */ m_dictionary_manager.add_directory( file_manager->getTranslationDir()); /* const std::set& languages = m_dictionary_manager.get_languages(); std::cout << "Number of languages: " << languages.size() << std::endl; for (std::set::const_iterator i = languages.begin(); i != languages.end(); ++i) { const Language& language = *i; std::cout << "Env: " << language.str() << std::endl << "Name: " << language.get_name() << std::endl << "Language: " << language.get_language() << std::endl << "Country: " << language.get_country() << std::endl << "Modifier: " << language.get_modifier() << std::endl << std::endl; } */ const char *p_language = getenv("LANGUAGE"); std::string language; if(p_language) { language=p_language; } else { const char *p_lang = getenv("LANG"); if(p_lang) language = p_lang; else { #ifdef WIN32 // Thanks to the frogatto developer for this code snippet: char c[1024]; GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, c, 1024); Log::verbose("translation", "GetLocaleInfo langname returns '%s'.", c); if(c[0]) { language = c; GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, c, 1024); Log::verbose("translation", "GetLocaleInfo tryname returns '%s'.", c); if(c[0]) language += std::string("_")+c; } // if c[0] #endif } // neither LANGUAGE nor LANG defined } if (language != "") { Log::verbose("translation", "Env var LANGUAGE = '%s'.", language.c_str()); if (language.find(":") != std::string::npos) { std::vector langs = StringUtils::split(language, ':'); Language l; for (unsigned int curr=0; currisRTLLanguage()) { const int FRIBIDI_BUFFER_SIZE = 512; FriBidiChar fribidiInput[FRIBIDI_BUFFER_SIZE]; int len = 0; int n = 0; //std::cout << "fribidi input : "; for (n = 0; ; n++) { fribidiInput[n] = in_ptr[n]; //std::cout << (int)fribidiInput[n] << " "; len++; if (n == FRIBIDI_BUFFER_SIZE-1) // prevent buffeoverflows { std::cerr << "WARNING : translated string too long, truncating!\n"; fribidiInput[n] = 0; break; } if (fribidiInput[n] == 0) break; // stop on '\0' } //std::cout << " (len=" << len << ")\n"; // Assume right to left as start direction. #if FRIBIDI_MINOR_VERSION==10 // While the doc for older fribidi versions is somewhat sparse, // using the RIGHT-TO-LEFT EMBEDDING character here appears to // work correct. FriBidiCharType pbase_dir = L'\u202B'; #else FriBidiCharType pbase_dir = FRIBIDI_PAR_ON; #endif static FriBidiChar fribidiOutput[FRIBIDI_BUFFER_SIZE]; for (n = 0; n < 512 ; n++) { fribidiOutput[n] = 0; } fribidi_boolean result = fribidi_log2vis(fribidiInput, len-1, &pbase_dir, fribidiOutput, /* gint *position_L_to_V_list */ NULL, /* gint *position_V_to_L_list */ NULL, /* gint8 *embedding_level_list */ NULL ); if (!result) { std::cerr << "Fribidi failed in 'fribidi_log2vis' =(\n"; m_converted_string = core::stringw(in_ptr); return m_converted_string.c_str(); } #ifdef WIN32 // On windows FriBidiChar is 4 bytes, but wchar_t is 2 bytes. // So we simply copy the characters over here (note that this // is technically incorrect, all characters we use/support fit // in 16 bits, which is what irrlicht supports atm). static wchar_t out[FRIBIDI_BUFFER_SIZE]; for(int i=0; i