mirror of
https://github.com/minetest/minetest.git
synced 2024-09-12 00:26:50 +02:00
Localize error messages in mainmenu (#11495)
Co-authored-by: sfan5 <sfan5@live.de> Co-authored-by: rubenwardy <rw@rubenwardy.com>
This commit is contained in:
parent
6910c8d920
commit
693f98373b
@ -63,7 +63,7 @@ function ui.update()
|
|||||||
-- handle errors
|
-- handle errors
|
||||||
if gamedata ~= nil and gamedata.reconnect_requested then
|
if gamedata ~= nil and gamedata.reconnect_requested then
|
||||||
local error_message = core.formspec_escape(
|
local error_message = core.formspec_escape(
|
||||||
gamedata.errormessage or "<none available>")
|
gamedata.errormessage or fgettext("<none available>"))
|
||||||
formspec = {
|
formspec = {
|
||||||
"size[14,8]",
|
"size[14,8]",
|
||||||
"real_coordinates[true]",
|
"real_coordinates[true]",
|
||||||
|
@ -37,6 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
#include "fontengine.h"
|
#include "fontengine.h"
|
||||||
#include "script/scripting_client.h"
|
#include "script/scripting_client.h"
|
||||||
|
#include "gettext.h"
|
||||||
|
|
||||||
#define CAMERA_OFFSET_STEP 200
|
#define CAMERA_OFFSET_STEP 200
|
||||||
#define WIELDMESH_OFFSET_X 55.0f
|
#define WIELDMESH_OFFSET_X 55.0f
|
||||||
@ -133,28 +134,6 @@ void Camera::notifyFovChange()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Camera::successfullyCreated(std::string &error_message)
|
|
||||||
{
|
|
||||||
if (!m_playernode) {
|
|
||||||
error_message = "Failed to create the player scene node";
|
|
||||||
} else if (!m_headnode) {
|
|
||||||
error_message = "Failed to create the head scene node";
|
|
||||||
} else if (!m_cameranode) {
|
|
||||||
error_message = "Failed to create the camera scene node";
|
|
||||||
} else if (!m_wieldmgr) {
|
|
||||||
error_message = "Failed to create the wielded item scene manager";
|
|
||||||
} else if (!m_wieldnode) {
|
|
||||||
error_message = "Failed to create the wielded item scene node";
|
|
||||||
} else {
|
|
||||||
error_message.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_client->modsLoaded())
|
|
||||||
m_client->getScript()->on_camera_ready(this);
|
|
||||||
|
|
||||||
return error_message.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the fractional part of x
|
// Returns the fractional part of x
|
||||||
inline f32 my_modf(f32 x)
|
inline f32 my_modf(f32 x)
|
||||||
{
|
{
|
||||||
|
@ -136,9 +136,6 @@ public:
|
|||||||
// Notify about new server-sent FOV and initialize smooth FOV transition
|
// Notify about new server-sent FOV and initialize smooth FOV transition
|
||||||
void notifyFovChange();
|
void notifyFovChange();
|
||||||
|
|
||||||
// Checks if the constructor was able to create the scene nodes
|
|
||||||
bool successfullyCreated(std::string &error_message);
|
|
||||||
|
|
||||||
// Step the camera: updates the viewing range and view bobbing.
|
// Step the camera: updates the viewing range and view bobbing.
|
||||||
void step(f32 dtime);
|
void step(f32 dtime);
|
||||||
|
|
||||||
|
@ -1282,9 +1282,8 @@ bool Game::createSingleplayerServer(const std::string &map_dir,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bind_addr.isIPv6() && !g_settings->getBool("enable_ipv6")) {
|
if (bind_addr.isIPv6() && !g_settings->getBool("enable_ipv6")) {
|
||||||
*error_message = "Unable to listen on " +
|
*error_message = fmtgettext("Unable to listen on %s because IPv6 is disabled",
|
||||||
bind_addr.serializeString() +
|
bind_addr.serializeString().c_str());
|
||||||
" because IPv6 is disabled";
|
|
||||||
errorstream << *error_message << std::endl;
|
errorstream << *error_message << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1317,7 +1316,7 @@ bool Game::createClient(const GameStartData &start_data)
|
|||||||
if (!could_connect) {
|
if (!could_connect) {
|
||||||
if (error_message->empty() && !connect_aborted) {
|
if (error_message->empty() && !connect_aborted) {
|
||||||
// Should not happen if error messages are set properly
|
// Should not happen if error messages are set properly
|
||||||
*error_message = "Connection failed for unknown reason";
|
*error_message = gettext("Connection failed for unknown reason");
|
||||||
errorstream << *error_message << std::endl;
|
errorstream << *error_message << std::endl;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -1326,7 +1325,7 @@ bool Game::createClient(const GameStartData &start_data)
|
|||||||
if (!getServerContent(&connect_aborted)) {
|
if (!getServerContent(&connect_aborted)) {
|
||||||
if (error_message->empty() && !connect_aborted) {
|
if (error_message->empty() && !connect_aborted) {
|
||||||
// Should not happen if error messages are set properly
|
// Should not happen if error messages are set properly
|
||||||
*error_message = "Connection failed for unknown reason";
|
*error_message = gettext("Connection failed for unknown reason");
|
||||||
errorstream << *error_message << std::endl;
|
errorstream << *error_message << std::endl;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -1342,8 +1341,8 @@ bool Game::createClient(const GameStartData &start_data)
|
|||||||
/* Camera
|
/* Camera
|
||||||
*/
|
*/
|
||||||
camera = new Camera(*draw_control, client, m_rendering_engine);
|
camera = new Camera(*draw_control, client, m_rendering_engine);
|
||||||
if (!camera->successfullyCreated(*error_message))
|
if (client->modsLoaded())
|
||||||
return false;
|
client->getScript()->on_camera_ready(camera);
|
||||||
client->setCamera(camera);
|
client->setCamera(camera);
|
||||||
|
|
||||||
/* Clouds
|
/* Clouds
|
||||||
@ -1456,15 +1455,14 @@ bool Game::connectToServer(const GameStartData &start_data,
|
|||||||
local_server_mode = true;
|
local_server_mode = true;
|
||||||
}
|
}
|
||||||
} catch (ResolveError &e) {
|
} catch (ResolveError &e) {
|
||||||
*error_message = std::string("Couldn't resolve address: ") + e.what();
|
*error_message = fmtgettext("Couldn't resolve address: %s", e.what());
|
||||||
|
|
||||||
errorstream << *error_message << std::endl;
|
errorstream << *error_message << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connect_address.isIPv6() && !g_settings->getBool("enable_ipv6")) {
|
if (connect_address.isIPv6() && !g_settings->getBool("enable_ipv6")) {
|
||||||
*error_message = "Unable to connect to " +
|
*error_message = fmtgettext("Unable to connect to %s because IPv6 is disabled", connect_address.serializeString().c_str());
|
||||||
connect_address.serializeString() +
|
|
||||||
" because IPv6 is disabled";
|
|
||||||
errorstream << *error_message << std::endl;
|
errorstream << *error_message << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1518,8 +1516,7 @@ bool Game::connectToServer(const GameStartData &start_data,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
if (client->accessDenied()) {
|
if (client->accessDenied()) {
|
||||||
*error_message = "Access denied. Reason: "
|
*error_message = fmtgettext("Access denied. Reason: %s", client->accessDeniedReason().c_str());
|
||||||
+ client->accessDeniedReason();
|
|
||||||
*reconnect_requested = client->reconnectRequested();
|
*reconnect_requested = client->reconnectRequested();
|
||||||
errorstream << *error_message << std::endl;
|
errorstream << *error_message << std::endl;
|
||||||
break;
|
break;
|
||||||
@ -1545,7 +1542,7 @@ bool Game::connectToServer(const GameStartData &start_data,
|
|||||||
wait_time += dtime;
|
wait_time += dtime;
|
||||||
// Only time out if we aren't waiting for the server we started
|
// Only time out if we aren't waiting for the server we started
|
||||||
if (!start_data.address.empty() && wait_time > 10) {
|
if (!start_data.address.empty() && wait_time > 10) {
|
||||||
*error_message = "Connection timed out.";
|
*error_message = gettext("Connection timed out.");
|
||||||
errorstream << *error_message << std::endl;
|
errorstream << *error_message << std::endl;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1593,7 +1590,7 @@ bool Game::getServerContent(bool *aborted)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (client->getState() < LC_Init) {
|
if (client->getState() < LC_Init) {
|
||||||
*error_message = "Client disconnected";
|
*error_message = gettext("Client disconnected");
|
||||||
errorstream << *error_message << std::endl;
|
errorstream << *error_message << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1675,8 +1672,7 @@ inline void Game::updateInteractTimers(f32 dtime)
|
|||||||
inline bool Game::checkConnection()
|
inline bool Game::checkConnection()
|
||||||
{
|
{
|
||||||
if (client->accessDenied()) {
|
if (client->accessDenied()) {
|
||||||
*error_message = "Access denied. Reason: "
|
*error_message = fmtgettext("Access denied. Reason: %s", client->accessDeniedReason().c_str());
|
||||||
+ client->accessDeniedReason();
|
|
||||||
*reconnect_requested = client->reconnectRequested();
|
*reconnect_requested = client->reconnectRequested();
|
||||||
errorstream << *error_message << std::endl;
|
errorstream << *error_message << std::endl;
|
||||||
return false;
|
return false;
|
||||||
@ -4351,14 +4347,15 @@ void the_game(bool *kill,
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (SerializationError &e) {
|
} catch (SerializationError &e) {
|
||||||
error_message = std::string("A serialization error occurred:\n")
|
const std::string ver_err = fmtgettext("The server is probably running a different version of %s.", PROJECT_NAME_C);
|
||||||
+ e.what() + "\n\nThe server is probably "
|
error_message = strgettext("A serialization error occurred:") +"\n"
|
||||||
" running a different version of " PROJECT_NAME_C ".";
|
+ e.what() + "\n\n" + ver_err;
|
||||||
errorstream << error_message << std::endl;
|
errorstream << error_message << std::endl;
|
||||||
} catch (ServerError &e) {
|
} catch (ServerError &e) {
|
||||||
error_message = e.what();
|
error_message = e.what();
|
||||||
errorstream << "ServerError: " << error_message << std::endl;
|
errorstream << "ServerError: " << error_message << std::endl;
|
||||||
} catch (ModError &e) {
|
} catch (ModError &e) {
|
||||||
|
// DO NOT TRANSLATE the `ModError`, it's used by ui.lua
|
||||||
error_message = std::string("ModError: ") + e.what() +
|
error_message = std::string("ModError: ") + e.what() +
|
||||||
strgettext("\nCheck debug.txt for details.");
|
strgettext("\nCheck debug.txt for details.");
|
||||||
errorstream << error_message << std::endl;
|
errorstream << error_message << std::endl;
|
||||||
|
@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
|
|
||||||
#include "config.h" // for USE_GETTEXT
|
#include "config.h" // for USE_GETTEXT
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include "porting.h"
|
||||||
|
|
||||||
#if USE_GETTEXT
|
#if USE_GETTEXT
|
||||||
#include <libintl.h>
|
#include <libintl.h>
|
||||||
@ -77,3 +78,31 @@ inline std::wstring fwgettext(const char *src, Args&&... args)
|
|||||||
delete[] str;
|
delete[] str;
|
||||||
return std::wstring(buf);
|
return std::wstring(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns translated string with format args applied
|
||||||
|
*
|
||||||
|
* @tparam Args Template parameter for format args
|
||||||
|
* @param format Translation source string
|
||||||
|
* @param args Variable format args
|
||||||
|
* @return translated string.
|
||||||
|
*/
|
||||||
|
template <typename ...Args>
|
||||||
|
inline std::string fmtgettext(const char *format, Args&&... args)
|
||||||
|
{
|
||||||
|
std::string buf;
|
||||||
|
std::size_t buf_size = 256;
|
||||||
|
buf.resize(buf_size);
|
||||||
|
|
||||||
|
format = gettext(format);
|
||||||
|
|
||||||
|
int len = porting::mt_snprintf(&buf[0], buf_size, format, std::forward<Args>(args)...);
|
||||||
|
if (len <= 0) throw std::runtime_error("gettext format error: " + std::string(format));
|
||||||
|
if ((size_t)len >= buf.size()) {
|
||||||
|
buf.resize(len+1); // extra null byte
|
||||||
|
porting::mt_snprintf(&buf[0], buf.size(), format, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
buf.resize(len); // remove null bytes
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
@ -33,6 +33,7 @@ set (UNITTEST_SRCS
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/test_voxelarea.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test_voxelarea.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test_voxelalgorithms.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test_voxelalgorithms.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test_voxelmanipulator.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test_voxelmanipulator.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/test_gettext.cpp
|
||||||
PARENT_SCOPE)
|
PARENT_SCOPE)
|
||||||
|
|
||||||
set (UNITTEST_CLIENT_SRCS
|
set (UNITTEST_CLIENT_SRCS
|
||||||
|
47
src/unittest/test_gettext.cpp
Normal file
47
src/unittest/test_gettext.cpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#include "test.h"
|
||||||
|
#include "porting.h"
|
||||||
|
#include "gettext.h"
|
||||||
|
|
||||||
|
class TestGettext : public TestBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TestGettext() {
|
||||||
|
TestManager::registerTestModule(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *getName() { return "TestGettext"; }
|
||||||
|
|
||||||
|
void runTests(IGameDef *gamedef);
|
||||||
|
|
||||||
|
void testSnfmtgettext();
|
||||||
|
void testFmtgettext();
|
||||||
|
};
|
||||||
|
|
||||||
|
static TestGettext g_test_instance;
|
||||||
|
|
||||||
|
void TestGettext::runTests(IGameDef *gamedef)
|
||||||
|
{
|
||||||
|
TEST(testFmtgettext);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestGettext::testFmtgettext()
|
||||||
|
{
|
||||||
|
std::string buf = fmtgettext("Viewing range changed to %d", 12);
|
||||||
|
UASSERTEQ(std::string, buf, "Viewing range changed to 12");
|
||||||
|
buf = fmtgettext(
|
||||||
|
"You are about to join this server with the name \"%s\" for the "
|
||||||
|
"first time.\n"
|
||||||
|
"If you proceed, a new account using your credentials will be "
|
||||||
|
"created on this server.\n"
|
||||||
|
"Please retype your password and click 'Register and Join' to "
|
||||||
|
"confirm account creation, or click 'Cancel' to abort."
|
||||||
|
, "A");
|
||||||
|
UASSERTEQ(std::string, buf,
|
||||||
|
"You are about to join this server with the name \"A\" for the "
|
||||||
|
"first time.\n"
|
||||||
|
"If you proceed, a new account using your credentials will be "
|
||||||
|
"created on this server.\n"
|
||||||
|
"Please retype your password and click 'Register and Join' to "
|
||||||
|
"confirm account creation, or click 'Cancel' to abort."
|
||||||
|
);
|
||||||
|
}
|
@ -61,6 +61,7 @@ xgettext --package-name=minetest \
|
|||||||
--keyword=wstrgettext \
|
--keyword=wstrgettext \
|
||||||
--keyword=core.gettext \
|
--keyword=core.gettext \
|
||||||
--keyword=showTranslatedStatusText \
|
--keyword=showTranslatedStatusText \
|
||||||
|
--keyword=fmtgettext \
|
||||||
--output $potfile \
|
--output $potfile \
|
||||||
--from-code=utf-8 \
|
--from-code=utf-8 \
|
||||||
`find src/ -name '*.cpp' -o -name '*.h'` \
|
`find src/ -name '*.cpp' -o -name '*.h'` \
|
||||||
|
Loading…
Reference in New Issue
Block a user