From 06d7b5deb4ec430606080362c5b77ae37e5db098 Mon Sep 17 00:00:00 2001 From: Mark Calderbank Date: Thu, 6 Dec 2007 14:01:13 +0000 Subject: [PATCH] New I18n system: client code --- i18n.cc | 104 +++++++++++++++-------------------------------- i18n.h | 45 ++++---------------- option.cc | 12 ++++-- option.h | 9 ++-- vdr.cc | 48 ++++++++++++++++++++++ vdr.h | 6 +++ vopts.cc | 21 +++++++++- vopts.h | 9 +++- vremoteconfig.cc | 4 +- woptionpane.cc | 3 +- wremoteconfig.cc | 4 +- 11 files changed, 141 insertions(+), 124 deletions(-) diff --git a/i18n.cc b/i18n.cc index f36d052..f27cd65 100644 --- a/i18n.cc +++ b/i18n.cc @@ -1,9 +1,5 @@ /* - * i18n.c: Internationalization - * - * This code is taken from the VDR project and modified for VOMP. - * See the main source file 'vdr.c' for original copyright information. - * Modifications (C) 2005 D Pickles. + Copyright 2007 Mark Calderbank This file is part of VOMP. @@ -23,84 +19,48 @@ */ #include "i18n.h" -#include "language-data.h" -#include "log.h" +#include +#include +#include + #include "vdr.h" +#include "log.h" -int I18n::LanguageID = DEFAULT_LANGUAGE_INDEX; +using namespace std; +I18n::trans_table I18n::Translations; int I18n::initialize(void) { VDR *vdr = VDR::getInstance(); - char *lang = vdr->configLoad("General", "Language"); - if (lang) - { - LanguageID = LanguageIndex(lang); - if (LanguageID == -1) - { - LanguageID = 0; - } - delete[] lang; - } + char *lang = vdr->configLoad("General", "LangCode"); + lang_code_list list = vdr->getLanguageList(); + string code; + if (lang && list.count(lang) > 0) + code = lang; else { - LanguageID = DEFAULT_LANGUAGE_INDEX; - } - return LanguageID; -} - -const char* I18n::translate(const char* s) -{ - if (LanguageID >= 0) - { - const tI18nPhrase *p = Phrases; - for (int i = ((p == Phrases) ? 1 : 2); i--; ) - { - for (; **p; p++) - { - if (strcmp(s, **p) == 0) - { - char *t = (*p)[LanguageID]; - if (t && *t) return t; - } - } - p = Phrases; - } - Log::getInstance()->log("I18n", Log::ERR, "No translation found for '%s' in language %d (%s)", - s, LanguageID, LanguageName(LanguageID)); - } - - char *p = strchr(s, '$'); - return p ? p + 1 : s; -} - -const char* const * I18n::CharSets(void) -{ - return charSets; -} - -const char* const I18n::LanguageCode(int Index) -{ - return 0 <= Index && Index < NumLanguages ? languageCodes[Index] : NULL; -} - -const char* I18n::LanguageName(int Index) -{ - return 0 <= Index && Index < NumLanguages ? Languages[Index] : NULL; -} - -int I18n::LanguageIndex(const char* Name) -{ - for (int i = 0; i < NumLanguages; i++) - { - if (STRCASESTR(Languages[i], Name)) return i; + // There is no LangCode in the config file, or the selected + // language isn't available on the server. + // Use 'en' if it exists on the server or if no languages + // are available. Otherwise use the first available language. + if (list.count("en") > 0 || list.empty()) + code = "en"; + else + code = list.begin()->first; + vdr->configSave("General", "LangCode", code.c_str()); } - Log::getInstance()->log("I18n", Log::ERR, "Unknown language: '%s'", Name); - return -1; + vdr->getLanguageContent(code, Translations); + return 1; } -int I18n::GetNumLanguages(void) +const char* I18n::translate(const char *s) { - return NumLanguages; + string str = s; + if (Translations.count(str) == 0) return s; + return Translations[str].c_str(); + // This isn't ideal. A call to initialize() invalidates + // the c_str(), so we need to make sure that no return + // values from this function are expected to remain + // valid after an initialize(). } diff --git a/i18n.h b/i18n.h index 77ff57a..096db48 100644 --- a/i18n.h +++ b/i18n.h @@ -1,9 +1,5 @@ /* - * i18n.h: Internationalization - * - * This code is taken from the VDR project and modified for VOMP. - * See the main source file 'vdr.c' for original copyright information. - * Modifications (C) 2005 D Pickles. + Copyright 2007 Mark Calderbank This file is part of VOMP. @@ -25,45 +21,22 @@ #ifndef I18N_H #define I18N_H -#include -#include -#ifdef WIN32 -#include -#include -#endif - -#include "defines.h" - - -#define I18N_HEADER -#include "language-data.h" -#undef I18N_HEADER +#include +#include #define tr(s) I18n::translate(s) class I18n { public: + typedef std::map lang_code_list; + typedef std::pair lang_code; + typedef std::map trans_table; + typedef std::pair trans_entry; static int initialize(void); - static const char* translate(const char* s); - - static const char* LanguageName(int index); - static int LanguageIndex(const char* name); - static int GetNumLanguages(void); - - const char* const * CharSets(void); - const char* const LanguageCode(int Index); - - const static int NumLanguages = NUM_LANGUAGES; - const static char* const Languages[]; + static const char* translate(const char *s); private: - static int LanguageID; - const static char* const charSets[]; - const static char* const languageCodes[]; - typedef char* tI18nPhrase[NumLanguages]; - const static tI18nPhrase Phrases[]; + static trans_table Translations; }; - - #endif diff --git a/option.cc b/option.cc index 310d3ba..22ffcea 100644 --- a/option.cc +++ b/option.cc @@ -22,9 +22,11 @@ #include "vdr.h" Option::Option(UINT ID, const char* DISPLAYTEXT, const char* CONFIGSECTION, const char* CONFIGKEY, UINT OPTIONTYPE, - UINT NUMCHOICES, UINT DEFAULTCHOICE, UINT STARTINT, const char * const * OPTIONS) + UINT NUMCHOICES, UINT DEFAULTCHOICE, UINT STARTINT, + const char * const * OPTIONS, const char * const * OPTIONKEYS) : id(ID), displayText(DISPLAYTEXT), configSection(CONFIGSECTION), configKey(CONFIGKEY), optionType(OPTIONTYPE), - numChoices(NUMCHOICES), defaultChoice(DEFAULTCHOICE), startInt(STARTINT), options(OPTIONS) + numChoices(NUMCHOICES), defaultChoice(DEFAULTCHOICE), startInt(STARTINT), + options(OPTIONS), optionkeys(OPTIONKEYS) { configChoice = defaultChoice; userSetChoice = defaultChoice; @@ -33,11 +35,13 @@ Option::Option(UINT ID, const char* DISPLAYTEXT, const char* CONFIGSECTION, cons char* config = VDR::getInstance()->configLoad(configSection, configKey); if (config) { - if (optionType == TYPE_TEXT) + if (optionType == TYPE_TEXT || optionType == TYPE_KEYED_TEXT) { + const char * const * list; + list = (optionType == TYPE_TEXT) ? options : optionkeys; for (UINT i = 0; i < numChoices; i++) { - if (!STRCASECMP(config, options[i])) + if (!STRCASECMP(config, list[i])) { configChoice = i; } diff --git a/option.h b/option.h index 00056a9..66a7a3b 100644 --- a/option.h +++ b/option.h @@ -27,7 +27,8 @@ class Option { public: Option(UINT id, const char* displayText, const char* configSection, const char* configKey, UINT optionType, - UINT numChoices, UINT defaultChoice, UINT startInt, const char * const * options); + UINT numChoices, UINT defaultChoice, UINT startInt, + const char * const * options, const char * const * optionkeys = NULL); UINT id; const char* displayText; @@ -38,12 +39,14 @@ class Option UINT defaultChoice; int startInt; const char * const * options; + const char * const * optionkeys; UINT configChoice; UINT userSetChoice; - const static UCHAR TYPE_TEXT = 1; - const static UCHAR TYPE_INT = 2; + const static UCHAR TYPE_TEXT = 1; + const static UCHAR TYPE_INT = 2; + const static UCHAR TYPE_KEYED_TEXT = 3; }; #endif diff --git a/vdr.cc b/vdr.cc index aa53d0e..5130846 100644 --- a/vdr.cc +++ b/vdr.cc @@ -1106,3 +1106,51 @@ int VDR::deleteTimer(RecTimer* delTimer) return toReturn; } +I18n::lang_code_list VDR::getLanguageList() +{ + I18n::lang_code_list CodeList; + CodeList["en"] = "English"; // Default entry + VDR_RequestPacket vrp; + if (!vrp.init(VDR_GETLANGUAGELIST, false, 0)) return CodeList; + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse() || vresp->end()) + { + delete vresp; + return CodeList; + } + CodeList.clear(); + while (!vresp->end()) + { + char* c_code = vresp->extractString(); + char* c_name = vresp->extractString(); + string code = c_code; + string name = c_name; + CodeList[code] = name; + delete[] c_code; + delete[] c_name; + } + delete vresp; + return CodeList; +} + +int VDR::getLanguageContent(const std::string code, I18n::trans_table& texts) +{ + VDR_RequestPacket vrp; + if (!vrp.init(VDR_GETLANGUAGECONTENT, false, 0)) return 0; + if (!vrp.addString(code.c_str())) return 0; + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse()) { delete vresp; return 0; } + texts.clear(); + while (!vresp->end()) + { + char* c_key = vresp->extractString(); + char* c_text = vresp->extractString(); + string key = c_key; + string text = c_text; + texts[key] = text; + delete[] c_key; + delete[] c_text; + } + delete vresp; + return 1; +} diff --git a/vdr.h b/vdr.h index ad3257c..d33ae1e 100644 --- a/vdr.h +++ b/vdr.h @@ -36,6 +36,7 @@ #include "mark.h" #include "media.h" #include "eventdispatcher.h" +#include "i18n.h" class TCP; class Log; @@ -182,6 +183,9 @@ class VDR : public Thread_TYPE, public EventDispatcher */ ULONG loadImage(const char * filename, ULONG xsize=0,ULONG ysize=0); + I18n::lang_code_list getLanguageList(); + int getLanguageContent(const string code, I18n::trans_table&); + // end protocol functions @@ -231,6 +235,8 @@ class VDR : public Thread_TYPE, public EventDispatcher const static ULONG VDR_GETMEDIALIST = 30; const static ULONG VDR_GETIMAGE = 31; const static ULONG VDR_GETIMAGEBLOCK = 32; + const static ULONG VDR_GETLANGUAGELIST = 33; + const static ULONG VDR_GETLANGUAGECONTENT = 34; protected: diff --git a/vopts.cc b/vopts.cc index 8342865..249c8f1 100644 --- a/vopts.cc +++ b/vopts.cc @@ -23,7 +23,6 @@ #include "colour.h" #include "video.h" #include "audio.h" -#include "i18n.h" #include "remote.h" #include "boxstack.h" #include "woptionpane.h" @@ -69,6 +68,18 @@ VOpts::VOpts() static const char* options13[] = {"1024", "2048", "4096", "8192", "16384", "32768", "65536"}; static const char* options14[] = {"No", "Yes"}; + // Get list of languages from VDR and construct options table + LangCode = VDR::getInstance()->getLanguageList(); + options2 = new const char*[LangCode.size()]; + options2keys = new const char*[LangCode.size()]; + I18n::lang_code_list::const_iterator iter; + UINT LangNumber = 0; + for (iter = LangCode.begin(); iter != LangCode.end(); ++iter,++LangNumber) + { + options2[LangNumber] = iter->second.c_str(); + options2keys[LangNumber] = iter->first.c_str(); + } + numPanes = 4; panes = new Boxx*[numPanes]; @@ -79,7 +90,7 @@ VOpts::VOpts() option = new Option(1, "Remote control type", "General", "Remote type", Option::TYPE_TEXT, 2, 0, 0, options1); options.push_back(option); wop->addOptionLine(option); - option = new Option(2, "Language", "General", "Language", Option::TYPE_TEXT, I18n::NumLanguages, 0, 0, I18n::Languages); + option = new Option(2, "Language", "General", "LangCode", Option::TYPE_KEYED_TEXT, LangCode.size(), 0, 0, options2, options2keys); options.push_back(option); wop->addOptionLine(option); option = new Option(3, "TV connection type", "TV", "Connection", Option::TYPE_TEXT, 2, 0, 0, options3); @@ -150,6 +161,8 @@ VOpts::~VOpts() delete[] panes; for(vector::iterator j = options.begin(); j != options.end(); j++) delete *j; + delete[] options2; + delete[] options2keys; } int VOpts::handleCommand(int command) @@ -211,6 +224,10 @@ void VOpts::doSave() { vdr->configSave(options[i]->configSection, options[i]->configKey, options[i]->options[options[i]->userSetChoice]); } + else if (options[i]->optionType == Option::TYPE_KEYED_TEXT) + { + vdr->configSave(options[i]->configSection, options[i]->configKey, options[i]->optionkeys[options[i]->userSetChoice]); + } else { char buffer[20]; diff --git a/vopts.h b/vopts.h index c6d1acd..fde4dfd 100644 --- a/vopts.h +++ b/vopts.h @@ -25,6 +25,7 @@ #include "tbboxx.h" #include "defines.h" #include "wtabbar.h" +#include "i18n.h" class Boxx; class Option; @@ -48,7 +49,11 @@ class VOpts : public TBBoxx vector options; + const char** options2; // Language list names + const char** options2keys; // Language list codes + I18n::lang_code_list LangCode; + // Although LangCode is only used in the constructor, it has to + // be valid for the lifetime of the VOpts instance, because we + // create Option objects with pointers into LangCode's data. }; - #endif - diff --git a/vremoteconfig.cc b/vremoteconfig.cc index 910d051..bb31246 100644 --- a/vremoteconfig.cc +++ b/vremoteconfig.cc @@ -114,11 +114,11 @@ void VRemoteConfig::draw() sl.draw(); if (learnmode) { - drawText(tr("Learning! Press any hardwired key to exit!"), 15, area.h - 30, Colour::SELECTHIGHLIGHT); + drawText(tr("Learning! Press any hardwired key to exit."), 15, area.h - 30, Colour::SELECTHIGHLIGHT); } else { - drawText(tr("Press [ok] for learning or MENU to reset to defaults! "), 15, area.h - 30, Colour::LIGHTTEXT); + drawText(tr("Press [ok] for learning or MENU to reset to defaults."), 15, area.h - 30, Colour::LIGHTTEXT); } diff --git a/woptionpane.cc b/woptionpane.cc index e54ac46..8003ad3 100644 --- a/woptionpane.cc +++ b/woptionpane.cc @@ -83,7 +83,8 @@ void WOptionPane::addOptionLine(Option* option) optionBoxes.resize(numOptions+1); optionBoxes[numOptions] = ob; - if (option->optionType == Option::TYPE_TEXT) + if (option->optionType == Option::TYPE_TEXT || + option->optionType == Option::TYPE_KEYED_TEXT) { for (UINT j = 0; j < option->numChoices; j++) { diff --git a/wremoteconfig.cc b/wremoteconfig.cc index 147c56d..9cf6f68 100644 --- a/wremoteconfig.cc +++ b/wremoteconfig.cc @@ -91,11 +91,11 @@ void WRemoteConfig::draw() if (learnmode) { - drawText(tr("Learning! Press any hardwired key to exit!"), 15, area.h - 30, Colour::SELECTHIGHLIGHT); + drawText(tr("Learning! Press any hardwired key to exit."), 15, area.h - 30, Colour::SELECTHIGHLIGHT); } else { - drawText(tr("Press [ok] for learning or MENU to reset to defaults! "), 15, area.h - 30, Colour::LIGHTTEXT); + drawText(tr("Press [ok] for learning or MENU to reset to defaults."), 15, area.h - 30, Colour::LIGHTTEXT); } } -- 2.39.2