This commit is contained in:
HarpyWar 2014-06-22 20:37:06 +04:00
parent 61d9c413cf
commit 05541ed49b
11 changed files with 338 additions and 2 deletions

View file

@ -36,6 +36,7 @@ set(BNETD_SOURCES
anongame_wol.cpp anongame_wol.h handle_wserv.cpp handle_wserv.h
luafunctions.cpp luafunctions.h luainterface.cpp luainterface.h
luaobjects.cpp luaobjects.h luawrapper.cpp luawrapper.h
i18n.cpp i18n.h
../win32/winmain.cpp ../win32/winmain.h
)

View file

@ -3763,7 +3763,9 @@ namespace pvpgn
std::string mode_str = args[1];
if (mode_str == "all")
mode = restart_mode_channels;
mode = restart_mode_all;
else if (mode_str == "i18n")
mode = restart_mode_i18n;
else if (mode_str == "channels")
mode = restart_mode_channels;
else if (mode_str == "realms")

236
src/bnetd/i18n.cpp Normal file
View file

@ -0,0 +1,236 @@
/*
* Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
*
* 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 2
* 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.
*/
#include "common/setup_before.h"
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cerrno>
#include <cstring>
#include <ctime>
#include <cstdlib>
#include <sstream>
#include <iostream>
#include <fstream>
#include <vector>
#include <utility>
#include <map>
#include <string.h>
#include "compat/strcasecmp.h"
#include "compat/snprintf.h"
#include "common/token.h"
#include "common/list.h"
#include "common/eventlog.h"
#include "common/xalloc.h"
#include "common/xstring.h"
#include "common/util.h"
#include "common/tag.h"
#include "common/pugixml.h"
#include "common/format.h"
#include "account.h"
#include "connection.h"
#include "message.h"
#include "helpfile.h"
#include "channel.h"
#include "i18n.h"
#include "prefs.h"
#include "common/setup_after.h"
namespace pvpgn
{
namespace bnetd
{
const char * commonfile = "common.xml"; // filename template, actually file name is "common-{lang}.xml"
/* Array with string translations, each string has array with pair language=translation
{
original = {
{ language = translate },
...
},
...
}
*/
std::map<std::string, std::map<t_gamelang, std::string> > translations = std::map<std::string, std::map<t_gamelang, std::string> >();
std::vector<t_gamelang> languages{
GAMELANG_ENGLISH_UINT, /* enUS */
GAMELANG_GERMAN_UINT, /* deDE */
GAMELANG_CZECH_UINT, /* csCZ */
GAMELANG_SPANISH_UINT, /* esES */
GAMELANG_FRENCH_UINT, /* frFR */
GAMELANG_ITALIAN_UINT, /* itIT */
GAMELANG_JAPANESE_UINT, /* jaJA */
GAMELANG_KOREAN_UINT, /* koKR */
GAMELANG_POLISH_UINT, /* plPL */
GAMELANG_RUSSIAN_UINT, /* ruRU */
GAMELANG_CHINESE_S_UINT, /* zhCN */
GAMELANG_CHINESE_T_UINT /* zhTW */
};
const char * _find_string(char const * text, t_gamelang gamelang);
extern int i18n_reload(void)
{
translations.clear();
i18n_load();
return 0;
}
extern int i18n_load(void)
{
const char * filename = buildpath(prefs_get_i18ndir(), commonfile);
std::string lang_filename;
pugi::xml_document doc;
std::string original, translate;
// iterate language list
for (std::vector<t_gamelang>::iterator lang = languages.begin(); lang != languages.end(); ++lang)
{
lang_filename = i18n_filename(filename, *lang);
if (FILE *f = fopen(lang_filename.c_str(), "r")) {
fclose(f);
if (!doc.load_file(lang_filename.c_str()))
{
ERROR1("could not parse localization file \"%s\"", lang_filename.c_str());
continue;
}
}
else {
// file not exists, ignore it
continue;
}
// root node
pugi::xml_node nodes = doc.child("strings");
// load xml strings to map
for (pugi::xml_node node = nodes.child("string"); node; node = node.next_sibling("string"))
{
original = node.child_value("original");
if (original[0] == '\0')
continue;
//std::map<const char *, std::map<t_gamelang, const char *> >::iterator it = translations.find(original);
// if not found then init
//if (it == translations.end())
// translations[original] = std::map<t_gamelang, const char *>();
// check if translate string has a reference to another translation
if (pugi::xml_attribute attr = node.child("translate").attribute("id"))
{
if (pugi::xml_node n = nodes.find_child_by_attribute("id", attr.value()))
translate = n.child_value("translate");
else
{
translate = original;
WARN2("could not find localization reference id=\"%s\", use original string (%s)", attr.value(), lang_filename.c_str());
}
}
else
{
translate = node.child_value("translate");
if (translate[0] == '\0')
{
translate = original;
WARN2("empty localization for \"%s\", use original string (%s)", original, lang_filename.c_str());
}
}
translations[original][*lang] = translate;
}
INFO1("localization file loaded \"%s\"", lang_filename.c_str());
}
return 0;
}
/* Find localized text for the given language */
const char * _find_string(char const * text, t_gamelang gamelang)
{
std::map<std::string, std::map<t_gamelang, std::string> >::iterator it = translations.find(text);
if (it != translations.end())
{
if (!it->second[gamelang].empty())
return it->second[gamelang].c_str();
}
return NULL;
}
/* Return text translation */
extern const char * localize(t_connection * c, char const * text)
{
t_gamelang gamelang = conn_get_gamelang(c);
if (!gamelang || !tag_check_gamelang(gamelang))
return text;
if (const char * translate = _find_string(text, gamelang))
return translate;
return text;
}
/* Add a locale tag into filename
example: motd.txt -> motd-ruRU.txt */
extern std::string i18n_filename(const char * filename, t_tag gamelang)
{
// get language string
char lang_str[sizeof(t_tag)+1];
std::memset(lang_str, 0, sizeof(lang_str));
tag_uint_to_str(lang_str, gamelang);
if (!tag_check_gamelang(gamelang))
{
ERROR1("got unknown language tag \"%s\"", lang_str);
return filename;
}
std::string _filename(filename);
// get extension
std::string::size_type idx(_filename.rfind('.'));
if (idx == std::string::npos || idx + 4 != _filename.size())
{
ERROR1("Invalid extension for '%s'", _filename.c_str());
return filename;
}
std::string ext(_filename.substr(idx + 1));
// get filename without extension
std::string fname(_filename.substr(0, idx));
std::string lang_filename(fname + "-" + lang_str + "." + ext);
return lang_filename;
}
}
}

58
src/bnetd/i18n.h Normal file
View file

@ -0,0 +1,58 @@
/*
* Copyright (C) 2014 HarpyWar (harpywar@gmail.com)
*
* 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 2
* 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.
*/
#ifndef INCLUDED_LOCALIZATION_TYPES
#define INCLUDED_LOCALIZATION_TYPES
namespace pvpgn
{
namespace bnetd
{
}
}
#endif
#ifndef JUST_NEED_TYPES
#ifndef INCLUDED_LOCALIZATION_PROTOS
#define INCLUDED_LOCALIZATION_PROTOS
#define JUST_NEED_TYPES
# include <string>
#undef JUST_NEED_TYPES
namespace pvpgn
{
namespace bnetd
{
extern int i18n_load(void);
extern int i18n_reload(void);
extern const char * localize(t_connection * c, char const * text);
extern std::string i18n_filename(const char * filename, t_tag gamelang);
}
}
/*****/
#endif
#endif

View file

@ -167,6 +167,7 @@ namespace pvpgn
{
lua::table config = bind.table();
config.update("filedir", prefs_get_filedir());
config.update("i18ndir", prefs_get_i18ndir());
config.update("scriptdir", prefs_get_scriptdir());
config.update("reportdir", prefs_get_reportdir());
config.update("chanlogdir", prefs_get_chanlogdir());

View file

@ -89,6 +89,7 @@
#include "realm.h"
#include "topic.h"
#include "handle_apireg.h"
#include "i18n.h"
#include "common/setup_after.h"
#ifdef WITH_LUA
@ -335,6 +336,8 @@ int pre_server_startup(void)
return STATUS_FDWATCH_FAILURE;
}
i18n_load();
connlist_create();
gamelist_create();
timerlist_create();
@ -382,7 +385,6 @@ int pre_server_startup(void)
eventlog(eventlog_level_error, __FUNCTION__, "could not load realm list");
topiclist_load(prefs_get_topicfile());
#ifdef WITH_LUA
lua_load(prefs_get_scriptdir());
#endif

View file

@ -39,6 +39,7 @@ namespace pvpgn
static struct {
/* files and paths */
char const * filedir;
char const * i18ndir;
char const * storage_path;
char const * logfile;
char const * loglevels;
@ -179,6 +180,10 @@ namespace pvpgn
static const char *conf_get_filedir(void);
static int conf_setdef_filedir(void);
static int conf_set_i18ndir(const char *valstr);
static const char *conf_get_i18ndir(void);
static int conf_setdef_i18ndir(void);
static int conf_set_storage_path(const char *valstr);
static const char *conf_get_storage_path(void);
static int conf_setdef_storage_path(void);
@ -703,6 +708,7 @@ namespace pvpgn
static t_conf_entry conf_table[] =
{
{ "filedir", conf_set_filedir, conf_get_filedir, conf_setdef_filedir },
{ "i18ndir", conf_set_i18ndir, conf_get_i18ndir, conf_setdef_i18ndir },
{ "storage_path", conf_set_storage_path, conf_get_storage_path, conf_setdef_storage_path },
{ "logfile", conf_set_logfile, conf_get_logfile, conf_setdef_logfile },
{ "loglevels", conf_set_loglevels, conf_get_loglevels, conf_setdef_loglevels },
@ -912,6 +918,27 @@ namespace pvpgn
}
extern char const * prefs_get_i18ndir(void)
{
return prefs_runtime_config.i18ndir;
}
static int conf_set_i18ndir(const char *valstr)
{
return conf_set_str(&prefs_runtime_config.i18ndir, valstr, NULL);
}
static int conf_setdef_i18ndir(void)
{
return conf_set_str(&prefs_runtime_config.i18ndir, NULL, BNETD_I18N_DIR);
}
static const char* conf_get_i18ndir(void)
{
return prefs_runtime_config.i18ndir;
}
extern char const * prefs_get_logfile(void)
{
return prefs_runtime_config.logfile;

View file

@ -37,6 +37,7 @@ namespace pvpgn
extern void prefs_unload(void);
extern char const * prefs_get_storage_path(void);
extern char const * prefs_get_filedir(void);
extern char const * prefs_get_i18ndir(void);
extern char const * prefs_get_logfile(void);
extern char const * prefs_get_loglevels(void);
extern char const * prefs_get_motdfile(void);

View file

@ -86,6 +86,7 @@
#include "icons.h"
#include "anongame_infos.h"
#include "topic.h"
#include "i18n.h"
#include "common/setup_after.h"
#ifdef WITH_LUA
@ -1373,6 +1374,11 @@ namespace pvpgn
attrlayer_load_default();
}
if (do_restart == restart_mode_all || do_restart == restart_mode_i18n)
{
i18n_reload();
}
if (do_restart == restart_mode_all || do_restart == restart_mode_channels)
{
channellist_reload();

View file

@ -76,6 +76,7 @@ namespace pvpgn
restart_mode_none, // always first (0)
restart_mode_all,
restart_mode_i18n,
restart_mode_channels,
restart_mode_realms,
restart_mode_autoupdate,

View file

@ -126,6 +126,7 @@ const bool BNETD_CHANLOG = false;
# define BNETD_DEFAULT_CONF_FILE "conf/bnetd.conf"
#endif
const char * const BNETD_FILE_DIR = "files";
const char * const BNETD_I18N_DIR = "conf/i18n";
const char * const BNETD_SCRIPT_DIR = "lua";
const char * const BNETD_STORAGE_PATH = "";
const char * const BNETD_REPORT_DIR = "reports";