add base for localization https://github.com/HarpyWar/pvpgn/issues/13
This commit is contained in:
parent
61d9c413cf
commit
05541ed49b
11 changed files with 338 additions and 2 deletions
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -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
236
src/bnetd/i18n.cpp
Normal 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
58
src/bnetd/i18n.h
Normal 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
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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";
|
||||
|
|
Loading…
Add table
Reference in a new issue