Skip to content

Commit

Permalink
add base for localization #13
Browse files Browse the repository at this point in the history
  • Loading branch information
HarpyWar committed Jun 22, 2014
1 parent 61d9c41 commit 05541ed
Show file tree
Hide file tree
Showing 11 changed files with 338 additions and 2 deletions.
1 change: 1 addition & 0 deletions src/bnetd/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
)

Expand Down
4 changes: 3 additions & 1 deletion src/bnetd/command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
236 changes: 236 additions & 0 deletions src/bnetd/i18n.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
/*
* Copyright (C) 2014 HarpyWar ([email protected])
*
* 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 changes: 58 additions & 0 deletions src/bnetd/i18n.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (C) 2014 HarpyWar ([email protected])
*
* 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
1 change: 1 addition & 0 deletions src/bnetd/luainterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
4 changes: 3 additions & 1 deletion src/bnetd/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -335,6 +336,8 @@ int pre_server_startup(void)
return STATUS_FDWATCH_FAILURE;
}

i18n_load();

connlist_create();
gamelist_create();
timerlist_create();
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit 05541ed

Please sign in to comment.