`minetest.translate`, but is in translation files.
* `@n` acts as a literal newline as well.
+Server side translations
+------------------------
+
+On some specific cases, server translation could be useful. For example, filter
+a list on labels and send results to client. A method is supplied to achieve
+that:
+
+`minetest.get_translated_string(lang_code, string)`: Translates `string` using
+translations for `lang_code` language. It gives the same result as if the string
+was translated by the client.
+The `lang_code` to use for a given player can be retrieved from
+the table returned by `minetest.get_player_information(name)`.
+IMPORTANT: This functionality should only be used for sorting, filtering or similar purposes.
+You do not need to use this to get translated strings to show up on the client.
Perlin noise
============
connection_uptime = 200, -- seconds since client connected
protocol_version = 32, -- protocol version used by client
formspec_version = 2, -- supported formspec version
+ lang_code = "fr" -- Language code used for translation
-- following information is available on debug build only!!!
-- DO NOT USE IN MODS
--ser_vers = 26, -- serialization version used by client
if (!name.empty()) {
TRACESTREAM(<< "Client: Loading translation: "
<< "\"" << filename << "\"" << std::endl);
- g_translations->loadTranslation(data);
+ g_client_translations->loadTranslation(data);
return true;
}
m_invert_mouse = g_settings->getBool("invert_mouse");
m_first_loop_after_window_activation = true;
- g_translations->clear();
+ g_client_translations->clear();
if (!init(map_dir, address, port, gamespec))
return false;
u8 getMinor() const { return m_version_minor; }
u8 getPatch() const { return m_version_patch; }
const std::string &getFull() const { return m_full_version; }
+
+ void setLangCode(const std::string &code) { m_lang_code = code; }
+ const std::string &getLangCode() const { return m_lang_code; }
private:
// Version is stored in here after INIT before INIT2
u8 m_pending_serialization_version = SER_FMT_VER_INVALID;
/* current state of client */
ClientState m_state = CS_Created;
+
+ // Client sent language code
+ std::string m_lang_code;
/*
Blocks that have been sent to client.
RemoteClient *client = getClient(peer_id, CS_InitDone);
+ // Keep client language for server translations
+ client->setLangCode(lang);
+
// Send active objects
{
PlayerSAO *sao = getPlayerSAO(peer_id);
#include "remoteplayer.h"
#include "server/luaentity_sao.h"
#include "server/player_sao.h"
+#include "util/string.h"
+#include "translation.h"
#ifndef SERVER
#include "client/client.h"
#endif
return 0;
}
+// get_translated_string(lang_code, string)
+int ModApiEnvMod::l_get_translated_string(lua_State * L)
+{
+ GET_ENV_PTR;
+ std::string lang_code = luaL_checkstring(L, 1);
+ std::string string = luaL_checkstring(L, 2);
+ getServer(L)->loadTranslationLanguage(lang_code);
+ string = wide_to_utf8(translate_string(utf8_to_wide(string),
+ &(*g_server_translations)[lang_code]));
+ lua_pushstring(L, string.c_str());
+ return 1;
+}
+
void ModApiEnvMod::Initialize(lua_State *L, int top)
{
API_FCT(set_node);
API_FCT(transforming_liquid_add);
API_FCT(forceload_block);
API_FCT(forceload_free_block);
+ API_FCT(get_translated_string);
}
void ModApiEnvMod::InitializeClient(lua_State *L, int top)
// stops forceloading a position
static int l_forceload_free_block(lua_State *L);
+ // Get a string translated server side
+ static int l_get_translated_string(lua_State * L);
+
public:
static void Initialize(lua_State *L, int top);
static void InitializeClient(lua_State *L, int top);
u16 prot_vers;
u8 ser_vers,major,minor,patch;
std::string vers_string;
+ std::string lang_code;
#define ERET(code) \
if (!(code)) { \
&avg_jitter))
ERET(getServer(L)->getClientInfo(player->getPeerId(), &state, &uptime, &ser_vers,
- &prot_vers, &major, &minor, &patch, &vers_string))
+ &prot_vers, &major, &minor, &patch, &vers_string, &lang_code))
lua_newtable(L);
int table = lua_gettop(L);
lua_pushnumber(L, player->formspec_version);
lua_settable(L, table);
+ lua_pushstring(L, "lang_code");
+ lua_pushstring(L, lang_code.c_str());
+ lua_settable(L, table);
+
#ifndef NDEBUG
lua_pushstring(L,"serialization_version");
lua_pushnumber(L, ser_vers);
#include "chat_interface.h"
#include "remoteplayer.h"
#include "server/player_sao.h"
+#include "translation.h"
class ClientNotFoundException : public BaseException
{
u8* major,
u8* minor,
u8* patch,
- std::string* vers_string
+ std::string* vers_string,
+ std::string* lang_code
)
{
*state = m_clients.getClientState(peer_id);
*minor = client->getMinor();
*patch = client->getPatch();
*vers_string = client->getFull();
+ *lang_code = client->getLangCode();
m_clients.unlock();
m_script->on_modchannel_message(channel, sender, message);
}
}
+
+void Server::loadTranslationLanguage(const std::string &lang_code)
+{
+ if (g_server_translations->count(lang_code))
+ return; // Already loaded
+
+ std::string suffix = "." + lang_code + ".tr";
+ for (const auto &i : m_media) {
+ if (str_ends_with(i.first, suffix)) {
+ std::ifstream t(i.second.path);
+ std::string data((std::istreambuf_iterator<char>(t)),
+ std::istreambuf_iterator<char>());
+
+ (*g_server_translations)[lang_code].loadTranslation(data);
+ }
+ }
+}
bool getClientConInfo(session_t peer_id, con::rtt_stat_type type, float *retval);
bool getClientInfo(session_t peer_id, ClientState *state, u32 *uptime,
u8* ser_vers, u16* prot_vers, u8* major, u8* minor, u8* patch,
- std::string* vers_string);
+ std::string* vers_string, std::string* lang_code);
void printToConsoleOnly(const std::string &text);
// Send block to specific player only
bool SendBlock(session_t peer_id, const v3s16 &blockpos);
+ // Load translations for a language
+ void loadTranslationLanguage(const std::string &lang_code);
+
// Bind address
Address m_bind_addr;
#include "translation.h"
#include "log.h"
#include "util/string.h"
+#include <unordered_map>
-static Translations main_translations;
-Translations *g_translations = &main_translations;
+
+#ifndef SERVER
+// Client translations
+Translations client_translations;
+Translations *g_client_translations = &client_translations;
+#endif
+
+// Per language server translations
+std::unordered_map<std::string,Translations> server_translations;
+std::unordered_map<std::string,Translations> *g_server_translations = &server_translations;
Translations::~Translations()
{
#include <string>
class Translations;
-extern Translations *g_translations;
+extern std::unordered_map<std::string, Translations> *g_server_translations;
+#ifndef SERVER
+extern Translations *g_client_translations;
+#endif
class Translations
{
* before filling it again.
*/
-void translate_all(const std::wstring &s, size_t &i, std::wstring &res);
+void translate_all(const std::wstring &s, size_t &i,
+ Translations *translations, std::wstring &res);
-void translate_string(const std::wstring &s, const std::wstring &textdomain,
- size_t &i, std::wstring &res) {
+void translate_string(const std::wstring &s, Translations *translations,
+ const std::wstring &textdomain, size_t &i, std::wstring &res)
+{
std::wostringstream output;
std::vector<std::wstring> args;
int arg_number = 1;
if (arg_number >= 10) {
errorstream << "Ignoring too many arguments to translation" << std::endl;
std::wstring arg;
- translate_all(s, i, arg);
+ translate_all(s, i, translations, arg);
args.push_back(arg);
continue;
}
output << arg_number;
++arg_number;
std::wstring arg;
- translate_all(s, i, arg);
+ translate_all(s, i, translations, arg);
args.push_back(arg);
} else {
// This is an escape sequence *inside* the template string to translate itself.
}
}
+ std::wstring toutput;
// Translate the template.
- std::wstring toutput = g_translations->getTranslation(textdomain, output.str());
+ if (translations != nullptr)
+ toutput = translations->getTranslation(
+ textdomain, output.str());
+ else
+ toutput = output.str();
// Put back the arguments in the translated template.
std::wostringstream result;
res = result.str();
}
-void translate_all(const std::wstring &s, size_t &i, std::wstring &res) {
+void translate_all(const std::wstring &s, size_t &i,
+ Translations *translations, std::wstring &res)
+{
std::wostringstream output;
while (i < s.length()) {
// Not an escape sequence: just add the character.
if (parts.size() > 1)
textdomain = parts[1];
std::wstring translated;
- translate_string(s, textdomain, i, translated);
+ translate_string(s, translations, textdomain, i, translated);
output << translated;
} else {
// Another escape sequence, such as colors. Preserve it.
res = output.str();
}
-std::wstring translate_string(const std::wstring &s) {
+// Translate string server side
+std::wstring translate_string(const std::wstring &s, Translations *translations)
+{
size_t i = 0;
std::wstring res;
- translate_all(s, i, res);
+ translate_all(s, i, translations, res);
return res;
}
+
+// Translate string client side
+std::wstring translate_string(const std::wstring &s)
+{
+#ifdef SERVER
+ return translate_string(s, nullptr);
+#else
+ return translate_string(s, g_client_translations);
+#endif
+}
#include <cctype>
#include <unordered_map>
+class Translations;
+
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
return tokens;
}
+std::wstring translate_string(const std::wstring &s, Translations *translations);
+
std::wstring translate_string(const std::wstring &s);
inline std::wstring unescape_translate(const std::wstring &s) {