Skip to content

Commit cee3c5e

Browse files
authoredApr 25, 2020
Add server side translations capability (#9733)
* Add server side translations capability
1 parent 914dbea commit cee3c5e

14 files changed

+126
-18
lines changed
 

Diff for: ‎doc/lua_api.txt

+15
Original file line numberDiff line numberDiff line change
@@ -3176,8 +3176,22 @@ Strings that need to be translated can contain several escapes, preceded by `@`.
31763176
`minetest.translate`, but is in translation files.
31773177
* `@n` acts as a literal newline as well.
31783178

3179+
Server side translations
3180+
------------------------
3181+
3182+
On some specific cases, server translation could be useful. For example, filter
3183+
a list on labels and send results to client. A method is supplied to achieve
3184+
that:
3185+
3186+
`minetest.get_translated_string(lang_code, string)`: Translates `string` using
3187+
translations for `lang_code` language. It gives the same result as if the string
3188+
was translated by the client.
31793189

3190+
The `lang_code` to use for a given player can be retrieved from
3191+
the table returned by `minetest.get_player_information(name)`.
31803192

3193+
IMPORTANT: This functionality should only be used for sorting, filtering or similar purposes.
3194+
You do not need to use this to get translated strings to show up on the client.
31813195

31823196
Perlin noise
31833197
============
@@ -4153,6 +4167,7 @@ Utilities
41534167
connection_uptime = 200, -- seconds since client connected
41544168
protocol_version = 32, -- protocol version used by client
41554169
formspec_version = 2, -- supported formspec version
4170+
lang_code = "fr" -- Language code used for translation
41564171
-- following information is available on debug build only!!!
41574172
-- DO NOT USE IN MODS
41584173
--ser_vers = 26, -- serialization version used by client

Diff for: ‎src/client/client.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -736,7 +736,7 @@ bool Client::loadMedia(const std::string &data, const std::string &filename)
736736
if (!name.empty()) {
737737
TRACESTREAM(<< "Client: Loading translation: "
738738
<< "\"" << filename << "\"" << std::endl);
739-
g_translations->loadTranslation(data);
739+
g_client_translations->loadTranslation(data);
740740
return true;
741741
}
742742

Diff for: ‎src/client/game.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1055,7 +1055,7 @@ bool Game::startup(bool *kill,
10551055
m_invert_mouse = g_settings->getBool("invert_mouse");
10561056
m_first_loop_after_window_activation = true;
10571057

1058-
g_translations->clear();
1058+
g_client_translations->clear();
10591059

10601060
if (!init(map_dir, address, port, gamespec))
10611061
return false;

Diff for: ‎src/clientiface.h

+6
Original file line numberDiff line numberDiff line change
@@ -339,12 +339,18 @@ class RemoteClient
339339
u8 getMinor() const { return m_version_minor; }
340340
u8 getPatch() const { return m_version_patch; }
341341
const std::string &getFull() const { return m_full_version; }
342+
343+
void setLangCode(const std::string &code) { m_lang_code = code; }
344+
const std::string &getLangCode() const { return m_lang_code; }
342345
private:
343346
// Version is stored in here after INIT before INIT2
344347
u8 m_pending_serialization_version = SER_FMT_VER_INVALID;
345348

346349
/* current state of client */
347350
ClientState m_state = CS_Created;
351+
352+
// Client sent language code
353+
std::string m_lang_code;
348354

349355
/*
350356
Blocks that have been sent to client.

Diff for: ‎src/network/serverpackethandler.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,9 @@ void Server::handleCommand_Init2(NetworkPacket* pkt)
311311

312312
RemoteClient *client = getClient(peer_id, CS_InitDone);
313313

314+
// Keep client language for server translations
315+
client->setLangCode(lang);
316+
314317
// Send active objects
315318
{
316319
PlayerSAO *sao = getPlayerSAO(peer_id);

Diff for: ‎src/script/lua_api/l_env.cpp

+16
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
4040
#include "remoteplayer.h"
4141
#include "server/luaentity_sao.h"
4242
#include "server/player_sao.h"
43+
#include "util/string.h"
44+
#include "translation.h"
4345
#ifndef SERVER
4446
#include "client/client.h"
4547
#endif
@@ -1302,6 +1304,19 @@ int ModApiEnvMod::l_forceload_free_block(lua_State *L)
13021304
return 0;
13031305
}
13041306

1307+
// get_translated_string(lang_code, string)
1308+
int ModApiEnvMod::l_get_translated_string(lua_State * L)
1309+
{
1310+
GET_ENV_PTR;
1311+
std::string lang_code = luaL_checkstring(L, 1);
1312+
std::string string = luaL_checkstring(L, 2);
1313+
getServer(L)->loadTranslationLanguage(lang_code);
1314+
string = wide_to_utf8(translate_string(utf8_to_wide(string),
1315+
&(*g_server_translations)[lang_code]));
1316+
lua_pushstring(L, string.c_str());
1317+
return 1;
1318+
}
1319+
13051320
void ModApiEnvMod::Initialize(lua_State *L, int top)
13061321
{
13071322
API_FCT(set_node);
@@ -1349,6 +1364,7 @@ void ModApiEnvMod::Initialize(lua_State *L, int top)
13491364
API_FCT(transforming_liquid_add);
13501365
API_FCT(forceload_block);
13511366
API_FCT(forceload_free_block);
1367+
API_FCT(get_translated_string);
13521368
}
13531369

13541370
void ModApiEnvMod::InitializeClient(lua_State *L, int top)

Diff for: ‎src/script/lua_api/l_env.h

+3
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,9 @@ class ModApiEnvMod : public ModApiBase {
187187
// stops forceloading a position
188188
static int l_forceload_free_block(lua_State *L);
189189

190+
// Get a string translated server side
191+
static int l_get_translated_string(lua_State * L);
192+
190193
public:
191194
static void Initialize(lua_State *L, int top);
192195
static void InitializeClient(lua_State *L, int top);

Diff for: ‎src/script/lua_api/l_server.cpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ int ModApiServer::l_get_player_information(lua_State *L)
163163
u16 prot_vers;
164164
u8 ser_vers,major,minor,patch;
165165
std::string vers_string;
166+
std::string lang_code;
166167

167168
#define ERET(code) \
168169
if (!(code)) { \
@@ -182,7 +183,7 @@ int ModApiServer::l_get_player_information(lua_State *L)
182183
&avg_jitter))
183184

184185
ERET(getServer(L)->getClientInfo(player->getPeerId(), &state, &uptime, &ser_vers,
185-
&prot_vers, &major, &minor, &patch, &vers_string))
186+
&prot_vers, &major, &minor, &patch, &vers_string, &lang_code))
186187

187188
lua_newtable(L);
188189
int table = lua_gettop(L);
@@ -237,6 +238,10 @@ int ModApiServer::l_get_player_information(lua_State *L)
237238
lua_pushnumber(L, player->formspec_version);
238239
lua_settable(L, table);
239240

241+
lua_pushstring(L, "lang_code");
242+
lua_pushstring(L, lang_code.c_str());
243+
lua_settable(L, table);
244+
240245
#ifndef NDEBUG
241246
lua_pushstring(L,"serialization_version");
242247
lua_pushnumber(L, ser_vers);

Diff for: ‎src/server.cpp

+21-1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
6464
#include "chat_interface.h"
6565
#include "remoteplayer.h"
6666
#include "server/player_sao.h"
67+
#include "translation.h"
6768

6869
class ClientNotFoundException : public BaseException
6970
{
@@ -1266,7 +1267,8 @@ bool Server::getClientInfo(
12661267
u8* major,
12671268
u8* minor,
12681269
u8* patch,
1269-
std::string* vers_string
1270+
std::string* vers_string,
1271+
std::string* lang_code
12701272
)
12711273
{
12721274
*state = m_clients.getClientState(peer_id);
@@ -1286,6 +1288,7 @@ bool Server::getClientInfo(
12861288
*minor = client->getMinor();
12871289
*patch = client->getPatch();
12881290
*vers_string = client->getFull();
1291+
*lang_code = client->getLangCode();
12891292

12901293
m_clients.unlock();
12911294

@@ -3937,3 +3940,20 @@ void Server::broadcastModChannelMessage(const std::string &channel,
39373940
m_script->on_modchannel_message(channel, sender, message);
39383941
}
39393942
}
3943+
3944+
void Server::loadTranslationLanguage(const std::string &lang_code)
3945+
{
3946+
if (g_server_translations->count(lang_code))
3947+
return; // Already loaded
3948+
3949+
std::string suffix = "." + lang_code + ".tr";
3950+
for (const auto &i : m_media) {
3951+
if (str_ends_with(i.first, suffix)) {
3952+
std::ifstream t(i.second.path);
3953+
std::string data((std::istreambuf_iterator<char>(t)),
3954+
std::istreambuf_iterator<char>());
3955+
3956+
(*g_server_translations)[lang_code].loadTranslation(data);
3957+
}
3958+
}
3959+
}

Diff for: ‎src/server.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ class Server : public con::PeerHandler, public MapEventReceiver,
334334
bool getClientConInfo(session_t peer_id, con::rtt_stat_type type, float *retval);
335335
bool getClientInfo(session_t peer_id, ClientState *state, u32 *uptime,
336336
u8* ser_vers, u16* prot_vers, u8* major, u8* minor, u8* patch,
337-
std::string* vers_string);
337+
std::string* vers_string, std::string* lang_code);
338338

339339
void printToConsoleOnly(const std::string &text);
340340

@@ -358,6 +358,9 @@ class Server : public con::PeerHandler, public MapEventReceiver,
358358
// Send block to specific player only
359359
bool SendBlock(session_t peer_id, const v3s16 &blockpos);
360360

361+
// Load translations for a language
362+
void loadTranslationLanguage(const std::string &lang_code);
363+
361364
// Bind address
362365
Address m_bind_addr;
363366

Diff for: ‎src/translation.cpp

+11-2
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
2020
#include "translation.h"
2121
#include "log.h"
2222
#include "util/string.h"
23+
#include <unordered_map>
2324

24-
static Translations main_translations;
25-
Translations *g_translations = &main_translations;
25+
26+
#ifndef SERVER
27+
// Client translations
28+
Translations client_translations;
29+
Translations *g_client_translations = &client_translations;
30+
#endif
31+
32+
// Per language server translations
33+
std::unordered_map<std::string,Translations> server_translations;
34+
std::unordered_map<std::string,Translations> *g_server_translations = &server_translations;
2635

2736
Translations::~Translations()
2837
{

Diff for: ‎src/translation.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
2323
#include <string>
2424

2525
class Translations;
26-
extern Translations *g_translations;
26+
extern std::unordered_map<std::string, Translations> *g_server_translations;
27+
#ifndef SERVER
28+
extern Translations *g_client_translations;
29+
#endif
2730

2831
class Translations
2932
{

Diff for: ‎src/util/string.cpp

+31-10
Original file line numberDiff line numberDiff line change
@@ -693,10 +693,12 @@ void str_replace(std::string &str, char from, char to)
693693
* before filling it again.
694694
*/
695695

696-
void translate_all(const std::wstring &s, size_t &i, std::wstring &res);
696+
void translate_all(const std::wstring &s, size_t &i,
697+
Translations *translations, std::wstring &res);
697698

698-
void translate_string(const std::wstring &s, const std::wstring &textdomain,
699-
size_t &i, std::wstring &res) {
699+
void translate_string(const std::wstring &s, Translations *translations,
700+
const std::wstring &textdomain, size_t &i, std::wstring &res)
701+
{
700702
std::wostringstream output;
701703
std::vector<std::wstring> args;
702704
int arg_number = 1;
@@ -750,15 +752,15 @@ void translate_string(const std::wstring &s, const std::wstring &textdomain,
750752
if (arg_number >= 10) {
751753
errorstream << "Ignoring too many arguments to translation" << std::endl;
752754
std::wstring arg;
753-
translate_all(s, i, arg);
755+
translate_all(s, i, translations, arg);
754756
args.push_back(arg);
755757
continue;
756758
}
757759
output.put(L'@');
758760
output << arg_number;
759761
++arg_number;
760762
std::wstring arg;
761-
translate_all(s, i, arg);
763+
translate_all(s, i, translations, arg);
762764
args.push_back(arg);
763765
} else {
764766
// This is an escape sequence *inside* the template string to translate itself.
@@ -767,8 +769,13 @@ void translate_string(const std::wstring &s, const std::wstring &textdomain,
767769
}
768770
}
769771

772+
std::wstring toutput;
770773
// Translate the template.
771-
std::wstring toutput = g_translations->getTranslation(textdomain, output.str());
774+
if (translations != nullptr)
775+
toutput = translations->getTranslation(
776+
textdomain, output.str());
777+
else
778+
toutput = output.str();
772779

773780
// Put back the arguments in the translated template.
774781
std::wostringstream result;
@@ -802,7 +809,9 @@ void translate_string(const std::wstring &s, const std::wstring &textdomain,
802809
res = result.str();
803810
}
804811

805-
void translate_all(const std::wstring &s, size_t &i, std::wstring &res) {
812+
void translate_all(const std::wstring &s, size_t &i,
813+
Translations *translations, std::wstring &res)
814+
{
806815
std::wostringstream output;
807816
while (i < s.length()) {
808817
// Not an escape sequence: just add the character.
@@ -851,7 +860,7 @@ void translate_all(const std::wstring &s, size_t &i, std::wstring &res) {
851860
if (parts.size() > 1)
852861
textdomain = parts[1];
853862
std::wstring translated;
854-
translate_string(s, textdomain, i, translated);
863+
translate_string(s, translations, textdomain, i, translated);
855864
output << translated;
856865
} else {
857866
// Another escape sequence, such as colors. Preserve it.
@@ -862,9 +871,21 @@ void translate_all(const std::wstring &s, size_t &i, std::wstring &res) {
862871
res = output.str();
863872
}
864873

865-
std::wstring translate_string(const std::wstring &s) {
874+
// Translate string server side
875+
std::wstring translate_string(const std::wstring &s, Translations *translations)
876+
{
866877
size_t i = 0;
867878
std::wstring res;
868-
translate_all(s, i, res);
879+
translate_all(s, i, translations, res);
869880
return res;
870881
}
882+
883+
// Translate string client side
884+
std::wstring translate_string(const std::wstring &s)
885+
{
886+
#ifdef SERVER
887+
return translate_string(s, nullptr);
888+
#else
889+
return translate_string(s, g_client_translations);
890+
#endif
891+
}

Diff for: ‎src/util/string.h

+4
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
3131
#include <cctype>
3232
#include <unordered_map>
3333

34+
class Translations;
35+
3436
#define STRINGIFY(x) #x
3537
#define TOSTRING(x) STRINGIFY(x)
3638

@@ -650,6 +652,8 @@ std::vector<std::basic_string<T> > split(const std::basic_string<T> &s, T delim)
650652
return tokens;
651653
}
652654

655+
std::wstring translate_string(const std::wstring &s, Translations *translations);
656+
653657
std::wstring translate_string(const std::wstring &s);
654658

655659
inline std::wstring unescape_translate(const std::wstring &s) {

0 commit comments

Comments
 (0)
Please sign in to comment.