X-Git-Url: https://git.verplant.org/?a=blobdiff_plain;ds=sidebyside;f=src%2Ftinygettext%2Ftinygettext.cpp;h=e780ded4233140329822334b44c87b413a113c56;hb=86e5914510fc29f4decbb66a7d602872b6c8555e;hp=0d3ec237b1ad8c759e82bf6a9d99c545fad03ab8;hpb=e6a940db5904743e8220491ce10b5107e119a44c;p=supertux.git diff --git a/src/tinygettext/tinygettext.cpp b/src/tinygettext/tinygettext.cpp index 0d3ec237b..e780ded42 100644 --- a/src/tinygettext/tinygettext.cpp +++ b/src/tinygettext/tinygettext.cpp @@ -1,7 +1,7 @@ // $Id$ -// -// TinyGetText - A small flexible gettext() replacement -// Copyright (C) 2004 Ingo Ruhnke +// +// TinyGetText +// Copyright (C) 2006 Ingo Ruhnke // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -12,23 +12,30 @@ // 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 #include -#include #include #include +#include #include #include +#include + +#ifndef GP2X +#include +#endif + #include "tinygettext.hpp" #include "log.hpp" #include "physfs/physfs_stream.hpp" -#include "log.hpp" +#include "findlocale.hpp" //#define TRANSLATION_DEBUG @@ -39,11 +46,29 @@ std::string convert(const std::string& text, const std::string& from_charset, const std::string& to_charset) { +#ifndef GP2X if (from_charset == to_charset) return text; - iconv_t cd = iconv_open(to_charset.c_str(), from_charset.c_str()); - + char *in = new char[text.length() + 1]; + strcpy(in, text.c_str()); + char *out = SDL_iconv_string(to_charset.c_str(), from_charset.c_str(), in, text.length() + 1); + delete[] in; + if(out == 0) + { + log_warning << "Error: conversion from " << from_charset << " to " << to_charset << " failed" << std::endl; + return ""; + } + std::string ret(out); + SDL_free(out); + return ret; +#else + log_warning << "FIXME: Char conversion not supported on GP2X!" << std::endl; + return ""; +#endif +#if 0 + iconv_t cd = SDL_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 @@ -56,7 +81,7 @@ std::string convert(const std::string& text, size_t out_len_temp = out_len; // iconv is counting down the bytes it has // written from this... - size_t retval = iconv(cd, &in, &in_len, &out, &out_len_temp); + size_t retval = SDL_iconv(cd, &in, &in_len, &out, &out_len_temp); out_len -= out_len_temp; // see above if (retval == (size_t) -1) { @@ -64,12 +89,13 @@ std::string convert(const std::string& text, log_warning << "Error: conversion from " << from_charset << " to " << to_charset << " went wrong: " << retval << std::endl; return ""; } - iconv_close(cd); + SDL_iconv_close(cd); std::string ret(out_orig, out_len); delete[] out_orig; delete[] in_orig; return ret; +#endif } bool has_suffix(const std::string& lhs, const std::string rhs) @@ -172,22 +198,30 @@ get_language_def(const std::string& name) 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 @@ -195,27 +229,27 @@ DictionaryManager::parseLocaleAliases() { // 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()) + while(isspace(static_cast(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()) { + while(!isspace(static_cast(c)) && !in.eof()) { alias += c; in.get(c); } - while(isspace(c) && !in.eof()) + while(isspace(static_cast(c)) && !in.eof()) in.get(c); std::string language; - while(!isspace(c) && !in.eof()) { + while(!isspace(static_cast(c)) && !in.eof()) { language += c; in.get(c); } @@ -225,11 +259,17 @@ DictionaryManager::parseLocaleAliases() 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()) { @@ -247,7 +287,7 @@ DictionaryManager::get_dictionary(const std::string& spec) for (SearchPath::iterator p = search_path.begin(); p != search_path.end(); ++p) { char** files = PHYSFS_enumerateFiles(p->c_str()); - if(!files) + if(!files) { log_warning << "Error: enumerateFiles() failed on " << *p << std::endl; } @@ -255,7 +295,25 @@ DictionaryManager::get_dictionary(const std::string& spec) { for(const char* const* filename = files; *filename != 0; filename++) { - if(std::string(*filename) == lang + ".po") { + + // 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); @@ -265,6 +323,7 @@ DictionaryManager::get_dictionary(const std::string& spec) log_warning << e.what() << "" << std::endl; } } + } PHYSFS_freeList(files); } @@ -296,14 +355,16 @@ DictionaryManager::get_languages() } 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)); } @@ -336,12 +397,21 @@ DictionaryManager::get_language_from_spec(const std::string& spec) 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 @@ -383,7 +453,7 @@ Dictionary::set_language(const LanguageDef& lang) } 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& msgstrs = i->second; @@ -428,7 +498,7 @@ Dictionary::translate(const char* msgid) } else { -#ifdef TRANSLATION_DBEUG +#ifdef TRANSLATION_DEBUG log_warning << "Couldn't translate: " << msgid << std::endl; #endif return msgid; @@ -436,7 +506,7 @@ Dictionary::translate(const char* 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()) @@ -445,13 +515,13 @@ Dictionary::translate(const std::string& msgid) } else { -#ifdef TRANSLATION_DBEUG +#ifdef TRANSLATION_DEBUG 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& msgstrs) @@ -461,8 +531,8 @@ Dictionary::add_translation(const std::string& msgid, const std::string& , 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; } @@ -470,47 +540,42 @@ Dictionary::add_translation(const std::string& msgid, const std::string& msgstr) class POFileReader { private: - struct Token - { - std::string keyword; - std::string content; - }; - Dictionary& dict; + std::istream& in; std::string from_charset; std::string to_charset; - std::string current_msgid; - std::string current_msgid_plural; - std::map msgstr_plural; - int line_num; - - enum { WANT_MSGID, WANT_MSGSTR, WANT_MSGSTR_PLURAL, WANT_MSGID_PLURAL } state; + int c; //TODO: char c? unsigned char c? + enum Token { + TOKEN_KEYWORD, //msgstr, msgid, etc. + TOKEN_CONTENT, //string literals, concatenated ("" "foo\n" "bar\n" -> "foo\nbar\n") + TOKEN_EOF //ran out of tokens + }; + Token token; + std::string tokenContent; //current contents of the keyword or string literal(s) public: - POFileReader(std::istream& in, Dictionary& dict_) - : dict(dict_) + POFileReader(std::istream& in_, Dictionary& dict_) + : in(in_), dict(dict_) { - 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(); + nextChar(); + if(c == 0xef) { // skip UTF-8 intro that some text editors produce + nextChar(); + nextChar(); + nextChar(); } - tokenize_po(in); + tokenize_po(); } void parse_header(const std::string& header) { - // Seperate the header in lines + // Separate the header in lines typedef std::vector Lines; Lines lines; - + std::string::size_type start = 0; for(std::string::size_type i = 0; i < header.length(); ++i) { @@ -542,179 +607,166 @@ public: } } - void add_token(const Token& token) + inline void nextChar() { - switch(state) - { - case WANT_MSGID: - if (token.keyword == "msgid") - { - current_msgid = token.content; - state = WANT_MSGID_PLURAL; - } - else if (token.keyword.empty()) - { - //log_warning << "Got EOF, everything looks ok." << std::endl; - } - else - { - 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") - { - current_msgid_plural = token.content; - state = WANT_MSGSTR_PLURAL; - } - else - { - state = WANT_MSGSTR; - add_token(token); - } - break; - - case WANT_MSGSTR: - if (token.keyword == "msgstr") - { - if (current_msgid == "") - { // .po Header is hidden in the msgid with the empty string - parse_header(token.content); - } - else - { - dict.add_translation(current_msgid, convert(token.content, from_charset, to_charset)); - } - state = WANT_MSGID; - } - else - { - log_warning << "tinygettext: expected 'msgstr' keyword, got " << token.keyword << " at line " << line_num << std::endl; - } - break; + c = in.get(); + if (c == '\n') + line_num++; + } - case WANT_MSGSTR_PLURAL: - if (has_prefix(token.keyword, "msgstr[")) - { - int num; - if (sscanf(token.keyword.c_str(), "msgstr[%d]", &num) != 1) - { - log_warning << "Error: Couldn't parse: " << token.keyword << std::endl; - } - else - { - msgstr_plural[num] = convert(token.content, from_charset, to_charset); - } - } - else - { - dict.add_translation(current_msgid, current_msgid_plural, msgstr_plural); + inline void skipSpace() + { + if(c == EOF) + return; - state = WANT_MSGID; - add_token(token); - } - break; + while(c == '#' || isspace(static_cast(c))) { + if(c == '#') { + while(c != '\n' && c != EOF) nextChar(); } + nextChar(); + } } - - inline int getchar(std::istream& in) - { - int c = in.get(); - if (c == '\n') - line_num += 1; - return c; + + inline bool expectToken(std::string type, Token wanted) { + if(token != wanted) { + log_warning << "Expected " << type << ", got "; + if(token == TOKEN_EOF) + log_warning << "EOF"; + else if(token == TOKEN_KEYWORD) + log_warning << "keyword '" << tokenContent << "'"; + else + log_warning << "string \"" << tokenContent << '"'; + + log_warning << " at line " << line_num << std::endl; + return false; + } + return true; } - - void tokenize_po(std::istream& in) - { - enum State { READ_KEYWORD, - READ_CONTENT, - READ_CONTENT_IN_STRING, - SKIP_COMMENT }; - State state = READ_KEYWORD; - int c; - Token token; + inline bool expectContent(std::string type, std::string wanted) { + if(tokenContent != wanted) { + log_warning << "Expected " << type << ", got "; + if(token == TOKEN_EOF) + log_warning << "EOF"; + else if(token == TOKEN_KEYWORD) + log_warning << "keyword '" << tokenContent << "'"; + else + log_warning << "string \"" << tokenContent << '"'; - while((c = getchar(in)) != EOF) - { - //log_debug << "Lexing char: " << char(c) << " " << state << std::endl; - switch(state) - { - case READ_KEYWORD: - if (c == '#') - { - state = SKIP_COMMENT; - } - else - { - // Read a new token - token = Token(); - - do { // Read keyword - token.keyword += c; - } while((c = getchar(in)) != EOF && !isspace(c)); - in.unget(); - - state = READ_CONTENT; - } - break; - - case READ_CONTENT: - while((c = getchar(in)) != EOF) - { - if (c == '"') { - // Found start of content - state = READ_CONTENT_IN_STRING; - break; - } else if (isspace(c)) { - // skip - } else { // Read something that may be a keyword - in.unget(); - state = READ_KEYWORD; - add_token(token); - break; - } - } - break; + log_warning << " at line " << line_num << std::endl; + return false; + } + return true; + } - case READ_CONTENT_IN_STRING: - if (c == '\\') { - c = getchar(in); - if (c != EOF) + void tokenize_po() + { + while((token = nextToken()) != TOKEN_EOF) + { + if(!expectToken("'msgid' keyword", TOKEN_KEYWORD) || !expectContent("'msgid' keyword", "msgid")) break; + + token = nextToken(); + if(!expectToken("name after msgid", TOKEN_CONTENT)) break; + std::string current_msgid = tokenContent; + + token = nextToken(); + if(!expectToken("msgstr or msgid_plural", TOKEN_KEYWORD)) break; + if(tokenContent == "msgid_plural") + { + //Plural form + token = nextToken(); + if(!expectToken("msgid_plural content", TOKEN_CONTENT)) break; + std::string current_msgid_plural = tokenContent; + + std::map msgstr_plural; + while((token = nextToken()) == TOKEN_KEYWORD && has_prefix(tokenContent, "msgstr[")) { - if (c == 'n') token.content += '\n'; - else if (c == 't') token.content += '\t'; - else if (c == 'r') token.content += '\r'; - else if (c == '"') token.content += '"'; - else + int num; + if (sscanf(tokenContent.c_str(), "msgstr[%d]", &num) != 1) { - log_warning << "Unhandled escape character: " << char(c) << std::endl; + log_warning << "Error: Couldn't parse: " << tokenContent << std::endl; } + + token = nextToken(); + if(!expectToken("msgstr[x] content", TOKEN_CONTENT)) break; + msgstr_plural[num] = convert(tokenContent, from_charset, to_charset); + } + dict.add_translation(current_msgid, current_msgid_plural, msgstr_plural); + } + else + { + // "Ordinary" translation + if(!expectContent("'msgstr' keyword", "msgstr")) break; + + token = nextToken(); + if(!expectToken("translation in msgstr", TOKEN_CONTENT)) break; + + if (current_msgid == "") + { // .po Header is hidden in the msgid with the empty string + parse_header(tokenContent); } else { - log_warning << "Unterminated string" << std::endl; + dict.add_translation(current_msgid, convert(tokenContent, from_charset, to_charset)); } - } else if (c == '"') { // Content string is terminated - state = READ_CONTENT; - } else { - token.content += c; } - break; + } + } + + Token nextToken() + { + //Clear token contents + tokenContent = ""; - case SKIP_COMMENT: - if (c == '\n') - state = READ_KEYWORD; - break; + skipSpace(); + + if(c == EOF) + return TOKEN_EOF; + else if(c != '"') + { + // Read a keyword + do { + tokenContent += c; + nextChar(); + } while(c != EOF && !isspace(static_cast(c))); + return TOKEN_KEYWORD; + } + else + { + do { + nextChar(); + // Read content + while(c != EOF && c != '"') { + if (c == '\\') { + nextChar(); + if (c == 'n') c = '\n'; + else if (c == 't') c = '\t'; + else if (c == 'r') c = '\r'; + else if (c == '"') c = '"'; + else if (c == '\\') c = '\\'; + else + { + log_warning << "Unhandled escape character: " << char(c) << std::endl; + c = ' '; + } + } + tokenContent += c; + nextChar(); + } + if(c == EOF) { + log_warning << "Unclosed string literal: " << tokenContent << std::endl; + return TOKEN_CONTENT; } + + // Read more strings? + skipSpace(); + } while(c == '"'); + return TOKEN_CONTENT; } - add_token(token); } }; -void read_po_file(Dictionary& dict_, std::istream& in) +void read_po_file(Dictionary& dict_, std::istream& in) { POFileReader reader(in, dict_); }