language.cpp

Go to the documentation of this file.
00001 /* $Id: language.cpp 26707 2008-05-18 17:29:12Z mordante $ */
00002 /* vim:set encoding=utf-8: */
00003 /*
00004    Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
00005    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License version 2
00009    or at your option any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 #include "global.hpp"
00017 
00018 #include "config.hpp"
00019 #include "filesystem.hpp"
00020 #include "foreach.hpp"
00021 #include "game_config.hpp"
00022 #include "gettext.hpp"
00023 #include "language.hpp"
00024 #include "log.hpp"
00025 #include "preferences.hpp"
00026 #include "util.hpp"
00027 #include "serialization/parser.hpp"
00028 #include "serialization/preprocessor.hpp"
00029 #include "wesconfig.h" //without this DUMMYLOCALES break
00030 
00031 #include <algorithm>
00032 #include <cassert>
00033 #include <cctype>
00034 #include <cerrno>
00035 #include <clocale>
00036 #include <cstdlib>
00037 #include <cstring>
00038 #include <iostream>
00039 #include <stdexcept>
00040 
00041 /** Tests one locale to be available. */
00042 static bool has_locale(const char* s) {
00043     try {
00044         // The way to find out whether a locale is available is to set it and
00045         // hope not runtime error gets thrown.
00046         std::locale dummy(s);
00047         return true;
00048     } catch (std::runtime_error&) {
00049         return false;
00050     }
00051 }       
00052 
00053 /** Test the locale for a language and it's utf-8 variations. */
00054 static bool has_language(const std::string& language)
00055 {
00056     if(has_locale(language.c_str())) {
00057         return true;
00058     }
00059 
00060     std::string utf = language + ".utf-8";
00061     if(has_locale(utf.c_str())) {
00062         return true;
00063     }
00064 
00065     utf = language + ".UTF-8";
00066     if(has_locale(utf.c_str())) {
00067         return true;
00068     }
00069     
00070     return false;
00071 }
00072 
00073 namespace {
00074     language_def current_language;
00075     string_map strings_;
00076 }
00077 
00078 static std::vector<language_def> known_languages;
00079 
00080 std::string languagedef_name (const language_def& def)
00081 {
00082     return def.language;
00083 }
00084 
00085 bool current_language_rtl()
00086 {
00087     return get_language().rtl;
00088 }
00089 
00090 bool language_def::operator== (const language_def& a) const
00091 {
00092     return ((language == a.language) /* && (localename == a.localename) */ );
00093 }
00094 
00095 bool language_def::available() const
00096 {
00097 #ifdef USE_DUMMYLOCALES
00098     // Dummy has every language available.
00099     return true;
00100 #else
00101     if(has_language(localename)) {
00102         return true;
00103     } else {
00104         foreach(const std::string& lang, alternates) {
00105             if(has_language(lang)) {
00106                 return true;
00107             }
00108         }
00109     }
00110     return false;
00111 #endif
00112 }
00113 
00114 symbol_table string_table;
00115 
00116 const t_string& symbol_table::operator[](const std::string& key) const
00117 {
00118     const string_map::const_iterator i = strings_.find(key);
00119     if(i != strings_.end()) {
00120         return i->second;
00121     } else {
00122         static t_string empty_string;
00123         // Let's do it the painful way (untlb means untranslatABLE).
00124         // It will cause problem if somebody stores more than one reference at once
00125         // but I don't really care since this path is an error path and it should
00126         // not have been taken in the first place. -- silene
00127         empty_string = "UNTLB " + key;
00128         return empty_string;
00129     }
00130 }
00131 
00132 const t_string& symbol_table::operator[](const char* key) const
00133 {
00134     return (*this)[std::string(key)];
00135 }
00136 
00137 bool load_language_list()
00138 {
00139     config cfg;
00140     try {
00141         scoped_istream stream = preprocess_file("data/hardwired/language.cfg");
00142         read(cfg, *stream);
00143     } catch(config::error &) {
00144         return false;
00145     }
00146 
00147     known_languages.clear();
00148     known_languages.push_back(
00149         language_def("", t_string(N_("System default language"), "wesnoth"), "ltr"));
00150 
00151     config::const_child_itors langs = cfg.child_range("locale");
00152     for(;langs.first != langs.second; ++langs.first) {
00153         known_languages.push_back(
00154             language_def((**langs.first)["locale"], (**langs.first)["name"], (**langs.first)["dir"],
00155                 (**langs.first)["alternates"]));
00156     }
00157 
00158     return true;
00159 }
00160 
00161 std::vector<language_def> get_languages()
00162 {
00163     return known_languages;
00164 }
00165 
00166 static void wesnoth_setlocale(int category, std::string const &slocale,
00167     std::vector<std::string> const *alternates)
00168 {
00169     char const *locale = slocale.c_str();
00170     // FIXME: ideally we should check LANGUAGE and on first invocation
00171     // use that value, so someone with es would get the game in Spanish
00172     // instead of en_US the first time round
00173     // LANGUAGE overrides other settings, so for now just get rid of it
00174     // FIXME: add configure check for unsetenv
00175 #ifndef _WIN32
00176 #ifndef __AMIGAOS4__
00177     unsetenv ("LANGUAGE"); // void so no return value to check
00178 #endif
00179 #endif
00180 
00181 #ifdef __BEOS__
00182     if(setenv ("LANG", locale, 1) == -1)
00183         std::cerr << "setenv LANG failed: " << strerror(errno);
00184     if(setenv ("LC_ALL", locale, 1) == -1)
00185         std::cerr << "setenv LC_ALL failed: " << strerror(errno);
00186 #endif
00187 #ifdef __APPLE__
00188     if(setenv ("LANGUAGE", locale, 1) == -1)
00189         std::cerr << "setenv LANGUAGE failed: " << strerror(errno);
00190     if(setenv ("LC_ALL", locale, 1) == -1)
00191         std::cerr << "setenv LC_ALL failed: " << strerror(errno);
00192 #endif
00193 
00194 #ifdef _WIN32
00195     const std::string env = "LANG=" + slocale;
00196     putenv(env.c_str());
00197 #endif
00198 
00199 #ifdef USE_DUMMYLOCALES
00200     static enum { UNINIT, NONE, PRESENT } status = UNINIT;
00201     static std::string locpath;
00202     if (status == UNINIT)
00203         if (char const *p = getenv("LOCPATH")) {
00204             locpath = p;
00205             status = PRESENT;
00206         } else status = NONE;
00207     if (slocale.empty())
00208         if (status == NONE)
00209             unsetenv("LOCPATH");
00210         else
00211             setenv("LOCPATH", locpath.c_str(), 1);
00212     else setenv("LOCPATH", (game_config::path + "/locales").c_str(), 1);
00213     std::string xlocale;
00214     if (!slocale.empty()) {
00215         // dummy suffix to prevent locale aliasing from kicking in
00216         xlocale = slocale + "@wesnoth";
00217         locale = xlocale.c_str();
00218     }
00219 #endif
00220 
00221     char *res = NULL;
00222     char const *try_loc = locale;
00223     std::vector<std::string>::const_iterator i;
00224     if (alternates) i = alternates->begin();
00225     while (true) {
00226         res = std::setlocale(category, try_loc);
00227         if (res) break;
00228 
00229         std::string utf8 = std::string(try_loc) + std::string(".utf-8");
00230         res = std::setlocale(category, utf8.c_str());
00231         if (res) break;
00232 
00233         utf8 = std::string(try_loc) + std::string(".UTF-8");
00234         res = std::setlocale(category, utf8.c_str());
00235         if (res) break;
00236 
00237         if (!alternates) break;
00238         if (i == alternates->end()) break;
00239         try_loc = i->c_str();
00240         i++;
00241     }
00242 
00243     if (res == NULL)
00244         std::cerr << "WARNING: setlocale() failed for '"
00245               << locale << "'.\n";
00246     else
00247         std::cerr << "set locale to '" << try_loc << "'\n";
00248 }
00249 
00250 bool set_language(const language_def& locale)
00251 {
00252     strings_.clear();
00253 
00254     std::string locale_lc;
00255     locale_lc.resize(locale.localename.size());
00256     std::transform(locale.localename.begin(),locale.localename.end(),locale_lc.begin(),tolower);
00257 
00258     config cfg;
00259 
00260     current_language = locale;
00261     wesnoth_setlocale(LC_MESSAGES, locale.localename, &locale.alternates);
00262     wesnoth_setlocale(LC_COLLATE, locale.localename, &locale.alternates);
00263 
00264     // fill string_table (should be moved somwhere else some day)
00265     try {
00266         scoped_istream stream = preprocess_file("data/hardwired/english.cfg");
00267         read(cfg, *stream);
00268     } catch(config::error& e) {
00269         std::cerr << "Could not read english.cfg\n";
00270         throw e;
00271     }
00272 
00273     config* langp = cfg.child("language");
00274     if (langp == NULL) {
00275         std::cerr << "No [language] block found in english.cfg\n";
00276         return false;
00277     }
00278 
00279     for(string_map::const_iterator j = langp->values.begin(); j != langp->values.end(); ++j) {
00280         strings_[j->first] = j->second;
00281     }
00282     // end of string_table fill
00283 
00284     // Reset translations for the name of current languages
00285     for (std::vector<language_def>::iterator itor = known_languages.begin();
00286             itor != known_languages.end(); ++itor) {
00287 
00288         itor->language.reset_translation();
00289     }
00290 
00291     return true;
00292 }
00293 
00294 const language_def& get_language() { return current_language; }
00295 
00296 const language_def& get_locale()
00297 {
00298     //TODO: Add in support for querying the locale on Windows
00299 
00300     assert(known_languages.size() != 0);
00301 
00302     const std::string& prefs_locale = preferences::language();
00303     if(prefs_locale.empty() == false) {
00304         wesnoth_setlocale(LC_MESSAGES, prefs_locale, NULL);
00305         for(std::vector<language_def>::const_iterator i = known_languages.begin();
00306                 i != known_languages.end(); ++i) {
00307             if (prefs_locale == i->localename)
00308                 return *i;
00309         }
00310         LOG_STREAM(info, general) << "'" << prefs_locale << "' locale not found in known array; defaulting to system locale\n";
00311         return known_languages[0];
00312     }
00313 
00314 #if 0
00315     const char* const locale = getenv("LANG");
00316     if(locale != NULL && strlen(locale) >= 2) {
00317         //we can't pass pointers into the string to the std::string
00318         //constructor because some STL implementations don't support
00319         //it (*cough* MSVC++6)
00320         std::string res(2,'z');
00321         res[0] = tolower(locale[0]);
00322         res[1] = tolower(locale[1]);
00323         return res;
00324     }
00325 #endif
00326 
00327     LOG_STREAM(info, general) << "locale could not be determined; defaulting to system locale\n";
00328     return known_languages[0];
00329 }
00330 
00331 void init_textdomains(const config& cfg)
00332 {
00333     config::const_child_itors t = cfg.child_range("textdomain");
00334 
00335     for(;t.first != t.second; ++t.first) {
00336         const std::string name = (**t.first)["name"];
00337         const std::string path = (**t.first)["path"];
00338 
00339         if(path.empty()) {
00340             t_string::add_textdomain(name, get_intl_dir());
00341         } else {
00342             const std::string& location = get_binary_file_location(path, "");
00343 
00344             //if location is empty, this causes a crash on Windows, so we
00345             //disallow adding empty domains
00346             if(location.empty()) {
00347                 std::cerr << "no location found for '" << path << "', not adding textdomain\n";
00348             } else {
00349                 t_string::add_textdomain(name, location);
00350             }
00351         }
00352     }
00353 }
00354 

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs