// $Id$
-//
-// TinyGetText - A small flexible gettext() replacement
-// Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
+//
+// TinyGetText
+// Copyright (C) 2006 Ingo Ruhnke <grumbel@gmx.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// 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.
+
#include <config.h>
#include <sys/types.h>
#include <iconv.h>
-#include <dirent.h>
#include <fstream>
#include <iostream>
+#include <algorithm>
#include <ctype.h>
#include <errno.h>
-#include "tinygettext.h"
+
+#include "tinygettext.hpp"
+#include "log.hpp"
+#include "physfs/physfs_stream.hpp"
+#include "log.hpp"
+#include "findlocale.hpp"
//#define TRANSLATION_DEBUG
/** Convert \a which is in \a from_charset to \a to_charset and return it */
std::string convert(const std::string& text,
const std::string& from_charset,
- const std::string& to_charset)
+ const std::string& to_charset)
{
if (from_charset == to_charset)
return text;
iconv_t cd = iconv_open(to_charset.c_str(), from_charset.c_str());
-
+
size_t in_len = text.length();
- size_t out_len = text.length()*3; // FIXME: cross fingers that this is enough
+ size_t out_len = text.length()*3; // FIXME: cross fingers that this is enough
char* out_orig = new char[out_len];
char* in_orig = new char[in_len+1];
strcpy(in_orig, text.c_str());
char* out = out_orig;
- char* in = in_orig;
+ ICONV_CONST char* in = in_orig;
size_t out_len_temp = out_len; // iconv is counting down the bytes it has
// written from this...
out_len -= out_len_temp; // see above
if (retval == (size_t) -1)
{
- std::cerr << strerror(errno) << std::endl;
- std::cerr << "Error: conversion from " << from_charset
- << " to " << to_charset << " went wrong: " << retval << std::endl;
+ log_warning << strerror(errno) << std::endl;
+ log_warning << "Error: conversion from " << from_charset << " to " << to_charset << " went wrong: " << retval << std::endl;
return "";
}
iconv_close(cd);
else if (name == "sk") return lang_sk;
else if (name == "pl") return lang_pl;
else if (name == "sl") return lang_sl;
- else return lang_en;
+ else return lang_en;
}
DictionaryManager::DictionaryManager()
: current_dict(&empty_dict)
{
parseLocaleAliases();
- // setup language from environment vars
- const char* lang = getenv("LC_ALL");
- if(!lang)
- lang = getenv("LC_MESSAGES");
- if(!lang)
- lang = getenv("LANG");
-
- if(lang)
- set_language(lang);
+ // Environment variable SUPERTUX_LANG overrides language settings.
+ const char* lang = getenv( "SUPERTUX_LANG" );
+ if( lang ){
+ set_language( lang );
+ return;
+ }
+ // use findlocale to setup language
+ FL_Locale *locale;
+ FL_FindLocale( &locale, FL_MESSAGES );
+ if(locale->lang) {
+ if (locale->country) {
+ set_language( std::string(locale->lang)+"_"+std::string(locale->country) );
+ } else {
+ set_language( std::string(locale->lang) );
+ }
+ }
+ FL_FreeLocale( &locale );
}
void
{
// try to parse language alias list
std::ifstream in("/usr/share/locale/locale.alias");
-
+
char c = ' ';
while(in.good() && !in.eof()) {
while(isspace(c) && !in.eof())
in.get(c);
-
+
if(c == '#') { // skip comments
while(c != '\n' && !in.eof())
in.get(c);
continue;
}
-
+
std::string alias;
while(!isspace(c) && !in.eof()) {
alias += c;
set_language_alias(alias, language);
}
}
-
+
Dictionary&
DictionaryManager::get_dictionary(const std::string& spec)
{
+
+ //log_debug << "Dictionary for language \"" << spec << "\" requested" << std::endl;
+
std::string lang = get_language_from_spec(spec);
+
+ //log_debug << "...normalized as \"" << lang << "\"" << std::endl;
+
Dictionaries::iterator i = dictionaries.find(get_language_from_spec(lang));
if (i != dictionaries.end())
{
}
else // Dictionary for languages lang isn't loaded, so we load it
{
- //std::cout << "get_dictionary: " << lang << std::endl;
+ //log_debug << "get_dictionary: " << lang << std::endl;
Dictionary& dict = dictionaries[lang];
dict.set_language(get_language_def(lang));
for (SearchPath::iterator p = search_path.begin(); p != search_path.end(); ++p)
{
- DIR* dir = opendir(p->c_str());
- if (!dir)
+ char** files = PHYSFS_enumerateFiles(p->c_str());
+ if(!files)
{
- std::cerr << "Error: opendir() failed on " << *p << std::endl;
+ log_warning << "Error: enumerateFiles() failed on " << *p << std::endl;
}
else
{
- struct dirent* ent;
- while((ent = readdir(dir)))
- {
- if (std::string(ent->d_name) == lang + ".po")
- {
- std::string pofile = *p + "/" + ent->d_name;
- std::ifstream in(pofile.c_str());
- if (!in)
- {
- std::cerr << "Error: Failure file opening: " << pofile << std::endl;
- }
- else
- {
- read_po_file(dict, in);
- }
- }
+ for(const char* const* filename = files;
+ *filename != 0; filename++) {
+
+ // check if filename matches requested language
+ std::string fname = std::string(*filename);
+ std::string load_from_file = "";
+ if(fname == lang + ".po") {
+ load_from_file = fname;
+ } else {
+ std::string::size_type s = lang.find("_");
+ if(s != std::string::npos) {
+ std::string lang_short = std::string(lang, 0, s);
+ if (fname == lang_short + ".po") {
+ load_from_file = lang_short;
+ }
+ }
+ }
+
+ // if it matched, load dictionary
+ if (load_from_file != "") {
+ //log_debug << "Loading dictionary for language \"" << lang << "\" from \"" << filename << "\"" << std::endl;
+ std::string pofile = *p + "/" + *filename;
+ try {
+ IFileStream in(pofile);
+ read_po_file(dict, in);
+ } catch(std::exception& e) {
+ log_warning << "Error: Failure file opening: " << pofile << std::endl;
+ log_warning << e.what() << "" << std::endl;
+ }
}
- closedir(dir);
+
+ }
+ PHYSFS_freeList(files);
}
}
for (SearchPath::iterator p = search_path.begin(); p != search_path.end(); ++p)
{
- DIR* dir = opendir(p->c_str());
- if (!dir)
+ char** files = PHYSFS_enumerateFiles(p->c_str());
+ if (!files)
{
- std::cerr << "Error: opendir() failed on " << *p << std::endl;
+ log_warning << "Error: opendir() failed on " << *p << std::endl;
}
else
{
- struct dirent* ent;
- while((ent = readdir(dir)))
- {
- if (has_suffix(ent->d_name, ".po"))
- {
- std::string filename = ent->d_name;
+ for(const char* const* file = files; *file != 0; file++) {
+ if(has_suffix(*file, ".po")) {
+ std::string filename = *file;
languages.insert(filename.substr(0, filename.length()-3));
- }
- }
- closedir(dir);
+ }
+ }
+ PHYSFS_freeList(files);
}
- }
+ }
return languages;
}
void
DictionaryManager::set_language(const std::string& lang)
{
+ //log_debug << "set_language \"" << lang << "\"" << std::endl;
language = get_language_from_spec(lang);
+ //log_debug << "==> \"" << language << "\"" << std::endl;
current_dict = & (get_dictionary(language));
}
+const std::string&
+DictionaryManager::get_language() const
+{
+ return language;
+}
+
void
DictionaryManager::set_charset(const std::string& charset)
{
if(i != language_aliases.end()) {
lang = i->second;
}
-
- std::string::size_type s = lang.find_first_of("_.");
- if(s == std::string::npos)
- return lang;
- return std::string(lang, 0, s);
+ std::string::size_type s = lang.find(".");
+ if(s != std::string::npos) {
+ lang = std::string(lang, 0, s);
+ }
+
+ s = lang.find("_");
+ if(s == std::string::npos) {
+ std::string lang_big = lang;
+ std::transform (lang_big.begin(), lang_big.end(), lang_big.begin(), toupper);
+ lang += "_" + lang_big;
+ }
+
+ return lang;
+
}
void
}
std::string
-Dictionary::translate(const std::string& msgid, const std::string& msgid2, int num)
+Dictionary::translate(const std::string& msgid, const std::string& msgid2, int num)
{
PluralEntries::iterator i = plural_entries.find(msgid);
std::map<int, std::string>& msgstrs = i->second;
else
{
#ifdef TRANSLATION_DEBUG
- std::cerr << "Warning: Couldn't translate: " << msgid << std::endl;
- std::cerr << "Candidates: " << std::endl;
+ log_warning << "Couldn't translate: " << msgid << std::endl;
+ log_warning << "Candidates: " << std::endl;
for (PluralEntries::iterator i = plural_entries.begin(); i != plural_entries.end(); ++i)
- std::cout << "'" << i->first << "'" << std::endl;
+ log_debug << "'" << i->first << "'" << std::endl;
#endif
if (plural2_1(num)) // default to english rules
}
}
+const char*
+Dictionary::translate(const char* msgid)
+{
+ Entries::iterator i = entries.find(msgid);
+ if (i != entries.end() && !i->second.empty())
+ {
+ return i->second.c_str();
+ }
+ else
+ {
+#ifdef TRANSLATION_DBEUG
+ log_warning << "Couldn't translate: " << msgid << std::endl;
+#endif
+ return msgid;
+ }
+}
+
std::string
-Dictionary::translate(const std::string& msgid)
+Dictionary::translate(const std::string& msgid)
{
Entries::iterator i = entries.find(msgid);
if (i != entries.end() && !i->second.empty())
else
{
#ifdef TRANSLATION_DBEUG
- std::cout << "Error: Couldn't translate: " << msgid << std::endl;
+ log_warning << "Couldn't translate: " << msgid << std::endl;
#endif
return msgid;
}
}
-
+
void
Dictionary::add_translation(const std::string& msgid, const std::string& ,
const std::map<int, std::string>& msgstrs)
plural_entries[msgid] = msgstrs;
}
-void
-Dictionary::add_translation(const std::string& msgid, const std::string& msgstr)
+void
+Dictionary::add_translation(const std::string& msgid, const std::string& msgstr)
{
entries[msgid] = msgstr;
}
{
state = WANT_MSGID;
line_num = 0;
+ char c = in.get();
+ if(c == (char) 0xef) { // skip UTF-8 intro that some texteditors produce
+ in.get();
+ in.get();
+ } else {
+ in.unget();
+ }
tokenize_po(in);
}
// Seperate the header in lines
typedef std::vector<std::string> Lines;
Lines lines;
-
+
std::string::size_type start = 0;
for(std::string::size_type i = 0; i < header.length(); ++i)
{
if (from_charset.empty() || from_charset == "CHARSET")
{
- std::cerr << "Error: Charset not specified for .po, fallback to ISO-8859-1" << std::endl;
+ log_warning << "Error: Charset not specified for .po, fallback to ISO-8859-1" << std::endl;
from_charset = "ISO-8859-1";
}
to_charset = dict.get_charset();
if (to_charset.empty())
- { // No charset requested from the dict, so we use the one from the .po
- to_charset = from_charset;
+ { // No charset requested from the dict, use utf-8
+ to_charset = "utf-8";
dict.set_charset(from_charset);
}
}
void add_token(const Token& token)
{
- switch(state)
+ switch(state)
{
case WANT_MSGID:
- if (token.keyword == "msgid")
+ if (token.keyword == "msgid")
{
current_msgid = token.content;
state = WANT_MSGID_PLURAL;
}
else if (token.keyword.empty())
{
- //std::cerr << "Got EOF, everything looks ok." << std::endl;
+ //log_warning << "Got EOF, everything looks ok." << std::endl;
}
else
{
- std::cerr << "tinygettext: expected 'msgid' keyword, got " << token.keyword
- << " at line " << line_num << std::endl;
+ log_warning << "tinygettext: expected 'msgid' keyword, got " << token.keyword << " at line " << line_num << std::endl;
}
break;
-
+
case WANT_MSGID_PLURAL:
- if (token.keyword == "msgid_plural")
+ if (token.keyword == "msgid_plural")
{
current_msgid_plural = token.content;
state = WANT_MSGSTR_PLURAL;
- }
+ }
else
{
state = WANT_MSGSTR;
break;
case WANT_MSGSTR:
- if (token.keyword == "msgstr")
+ if (token.keyword == "msgstr")
{
- if (current_msgid == "")
+ if (current_msgid == "")
{ // .po Header is hidden in the msgid with the empty string
parse_header(token.content);
}
dict.add_translation(current_msgid, convert(token.content, from_charset, to_charset));
}
state = WANT_MSGID;
- }
+ }
else
{
- std::cerr << "tinygettext: expected 'msgstr' keyword, got " << token.keyword
- << " at line " << line_num << std::endl;
+ log_warning << "tinygettext: expected 'msgstr' keyword, got " << token.keyword << " at line " << line_num << std::endl;
}
break;
case WANT_MSGSTR_PLURAL:
- if (has_prefix(token.keyword, "msgstr["))
+ if (has_prefix(token.keyword, "msgstr["))
{
int num;
- if (sscanf(token.keyword.c_str(), "msgstr[%d]", &num) != 1)
+ if (sscanf(token.keyword.c_str(), "msgstr[%d]", &num) != 1)
{
- std::cerr << "Error: Couldn't parse: " << token.keyword << std::endl;
- }
- else
+ log_warning << "Error: Couldn't parse: " << token.keyword << std::endl;
+ }
+ else
{
msgstr_plural[num] = convert(token.content, from_charset, to_charset);
}
}
- else
+ else
{
dict.add_translation(current_msgid, current_msgid_plural, msgstr_plural);
break;
}
}
-
- inline int getchar(std::istream& in)
+
+ inline int getchar(std::istream& in)
{
int c = in.get();
if (c == '\n')
line_num += 1;
return c;
}
-
+
void tokenize_po(std::istream& in)
{
- enum State { READ_KEYWORD,
+ enum State { READ_KEYWORD,
READ_CONTENT,
READ_CONTENT_IN_STRING,
SKIP_COMMENT };
while((c = getchar(in)) != EOF)
{
- //std::cout << "Lexing char: " << char(c) << " " << state << std::endl;
+ //log_debug << "Lexing char: " << char(c) << " " << state << std::endl;
switch(state)
{
case READ_KEYWORD:
{
// Read a new token
token = Token();
-
- do { // Read keyword
+
+ do { // Read keyword
token.keyword += c;
} while((c = getchar(in)) != EOF && !isspace(c));
in.unget();
case READ_CONTENT:
while((c = getchar(in)) != EOF)
{
- if (c == '"') {
+ if (c == '"') {
// Found start of content
state = READ_CONTENT_IN_STRING;
break;
else if (c == 't') token.content += '\t';
else if (c == 'r') token.content += '\r';
else if (c == '"') token.content += '"';
+ else if (c == '\\') token.content += '\\';
else
{
- std::cout << "Unhandled escape character: " << char(c) << std::endl;
+ log_warning << "Unhandled escape character: " << char(c) << std::endl;
}
}
else
{
- std::cout << "Unterminated string" << std::endl;
+ log_warning << "Unterminated string" << std::endl;
}
} else if (c == '"') { // Content string is terminated
state = READ_CONTENT;
}
};
-void read_po_file(Dictionary& dict_, std::istream& in)
+void read_po_file(Dictionary& dict_, std::istream& in)
{
POFileReader reader(in, dict_);
}