Skip to content

Commit 693f983

Browse files
snowyusfan5rubenwardy
authored
Localize error messages in mainmenu (#11495)
Co-authored-by: sfan5 <sfan5@live.de> Co-authored-by: rubenwardy <rw@rubenwardy.com>
1 parent 6910c8d commit 693f983

File tree

8 files changed

+97
-46
lines changed

8 files changed

+97
-46
lines changed

builtin/fstk/ui.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ function ui.update()
6363
-- handle errors
6464
if gamedata ~= nil and gamedata.reconnect_requested then
6565
local error_message = core.formspec_escape(
66-
gamedata.errormessage or "<none available>")
66+
gamedata.errormessage or fgettext("<none available>"))
6767
formspec = {
6868
"size[14,8]",
6969
"real_coordinates[true]",

src/client/camera.cpp

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
3737
#include "constants.h"
3838
#include "fontengine.h"
3939
#include "script/scripting_client.h"
40+
#include "gettext.h"
4041

4142
#define CAMERA_OFFSET_STEP 200
4243
#define WIELDMESH_OFFSET_X 55.0f
@@ -133,28 +134,6 @@ void Camera::notifyFovChange()
133134
}
134135
}
135136

136-
bool Camera::successfullyCreated(std::string &error_message)
137-
{
138-
if (!m_playernode) {
139-
error_message = "Failed to create the player scene node";
140-
} else if (!m_headnode) {
141-
error_message = "Failed to create the head scene node";
142-
} else if (!m_cameranode) {
143-
error_message = "Failed to create the camera scene node";
144-
} else if (!m_wieldmgr) {
145-
error_message = "Failed to create the wielded item scene manager";
146-
} else if (!m_wieldnode) {
147-
error_message = "Failed to create the wielded item scene node";
148-
} else {
149-
error_message.clear();
150-
}
151-
152-
if (m_client->modsLoaded())
153-
m_client->getScript()->on_camera_ready(this);
154-
155-
return error_message.empty();
156-
}
157-
158137
// Returns the fractional part of x
159138
inline f32 my_modf(f32 x)
160139
{

src/client/camera.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,6 @@ class Camera
136136
// Notify about new server-sent FOV and initialize smooth FOV transition
137137
void notifyFovChange();
138138

139-
// Checks if the constructor was able to create the scene nodes
140-
bool successfullyCreated(std::string &error_message);
141-
142139
// Step the camera: updates the viewing range and view bobbing.
143140
void step(f32 dtime);
144141

src/client/game.cpp

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,9 +1282,8 @@ bool Game::createSingleplayerServer(const std::string &map_dir,
12821282
}
12831283

12841284
if (bind_addr.isIPv6() && !g_settings->getBool("enable_ipv6")) {
1285-
*error_message = "Unable to listen on " +
1286-
bind_addr.serializeString() +
1287-
" because IPv6 is disabled";
1285+
*error_message = fmtgettext("Unable to listen on %s because IPv6 is disabled",
1286+
bind_addr.serializeString().c_str());
12881287
errorstream << *error_message << std::endl;
12891288
return false;
12901289
}
@@ -1317,7 +1316,7 @@ bool Game::createClient(const GameStartData &start_data)
13171316
if (!could_connect) {
13181317
if (error_message->empty() && !connect_aborted) {
13191318
// Should not happen if error messages are set properly
1320-
*error_message = "Connection failed for unknown reason";
1319+
*error_message = gettext("Connection failed for unknown reason");
13211320
errorstream << *error_message << std::endl;
13221321
}
13231322
return false;
@@ -1326,7 +1325,7 @@ bool Game::createClient(const GameStartData &start_data)
13261325
if (!getServerContent(&connect_aborted)) {
13271326
if (error_message->empty() && !connect_aborted) {
13281327
// Should not happen if error messages are set properly
1329-
*error_message = "Connection failed for unknown reason";
1328+
*error_message = gettext("Connection failed for unknown reason");
13301329
errorstream << *error_message << std::endl;
13311330
}
13321331
return false;
@@ -1342,8 +1341,8 @@ bool Game::createClient(const GameStartData &start_data)
13421341
/* Camera
13431342
*/
13441343
camera = new Camera(*draw_control, client, m_rendering_engine);
1345-
if (!camera->successfullyCreated(*error_message))
1346-
return false;
1344+
if (client->modsLoaded())
1345+
client->getScript()->on_camera_ready(camera);
13471346
client->setCamera(camera);
13481347

13491348
/* Clouds
@@ -1456,15 +1455,14 @@ bool Game::connectToServer(const GameStartData &start_data,
14561455
local_server_mode = true;
14571456
}
14581457
} catch (ResolveError &e) {
1459-
*error_message = std::string("Couldn't resolve address: ") + e.what();
1458+
*error_message = fmtgettext("Couldn't resolve address: %s", e.what());
1459+
14601460
errorstream << *error_message << std::endl;
14611461
return false;
14621462
}
14631463

14641464
if (connect_address.isIPv6() && !g_settings->getBool("enable_ipv6")) {
1465-
*error_message = "Unable to connect to " +
1466-
connect_address.serializeString() +
1467-
" because IPv6 is disabled";
1465+
*error_message = fmtgettext("Unable to connect to %s because IPv6 is disabled", connect_address.serializeString().c_str());
14681466
errorstream << *error_message << std::endl;
14691467
return false;
14701468
}
@@ -1518,8 +1516,7 @@ bool Game::connectToServer(const GameStartData &start_data,
15181516
break;
15191517

15201518
if (client->accessDenied()) {
1521-
*error_message = "Access denied. Reason: "
1522-
+ client->accessDeniedReason();
1519+
*error_message = fmtgettext("Access denied. Reason: %s", client->accessDeniedReason().c_str());
15231520
*reconnect_requested = client->reconnectRequested();
15241521
errorstream << *error_message << std::endl;
15251522
break;
@@ -1545,7 +1542,7 @@ bool Game::connectToServer(const GameStartData &start_data,
15451542
wait_time += dtime;
15461543
// Only time out if we aren't waiting for the server we started
15471544
if (!start_data.address.empty() && wait_time > 10) {
1548-
*error_message = "Connection timed out.";
1545+
*error_message = gettext("Connection timed out.");
15491546
errorstream << *error_message << std::endl;
15501547
break;
15511548
}
@@ -1593,7 +1590,7 @@ bool Game::getServerContent(bool *aborted)
15931590
return false;
15941591

15951592
if (client->getState() < LC_Init) {
1596-
*error_message = "Client disconnected";
1593+
*error_message = gettext("Client disconnected");
15971594
errorstream << *error_message << std::endl;
15981595
return false;
15991596
}
@@ -1675,8 +1672,7 @@ inline void Game::updateInteractTimers(f32 dtime)
16751672
inline bool Game::checkConnection()
16761673
{
16771674
if (client->accessDenied()) {
1678-
*error_message = "Access denied. Reason: "
1679-
+ client->accessDeniedReason();
1675+
*error_message = fmtgettext("Access denied. Reason: %s", client->accessDeniedReason().c_str());
16801676
*reconnect_requested = client->reconnectRequested();
16811677
errorstream << *error_message << std::endl;
16821678
return false;
@@ -4351,14 +4347,15 @@ void the_game(bool *kill,
43514347
}
43524348

43534349
} catch (SerializationError &e) {
4354-
error_message = std::string("A serialization error occurred:\n")
4355-
+ e.what() + "\n\nThe server is probably "
4356-
" running a different version of " PROJECT_NAME_C ".";
4350+
const std::string ver_err = fmtgettext("The server is probably running a different version of %s.", PROJECT_NAME_C);
4351+
error_message = strgettext("A serialization error occurred:") +"\n"
4352+
+ e.what() + "\n\n" + ver_err;
43574353
errorstream << error_message << std::endl;
43584354
} catch (ServerError &e) {
43594355
error_message = e.what();
43604356
errorstream << "ServerError: " << error_message << std::endl;
43614357
} catch (ModError &e) {
4358+
// DO NOT TRANSLATE the `ModError`, it's used by ui.lua
43624359
error_message = std::string("ModError: ") + e.what() +
43634360
strgettext("\nCheck debug.txt for details.");
43644361
errorstream << error_message << std::endl;

src/gettext.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
2121

2222
#include "config.h" // for USE_GETTEXT
2323
#include <string>
24+
#include "porting.h"
2425

2526
#if USE_GETTEXT
2627
#include <libintl.h>
@@ -77,3 +78,31 @@ inline std::wstring fwgettext(const char *src, Args&&... args)
7778
delete[] str;
7879
return std::wstring(buf);
7980
}
81+
82+
/**
83+
* Returns translated string with format args applied
84+
*
85+
* @tparam Args Template parameter for format args
86+
* @param format Translation source string
87+
* @param args Variable format args
88+
* @return translated string.
89+
*/
90+
template <typename ...Args>
91+
inline std::string fmtgettext(const char *format, Args&&... args)
92+
{
93+
std::string buf;
94+
std::size_t buf_size = 256;
95+
buf.resize(buf_size);
96+
97+
format = gettext(format);
98+
99+
int len = porting::mt_snprintf(&buf[0], buf_size, format, std::forward<Args>(args)...);
100+
if (len <= 0) throw std::runtime_error("gettext format error: " + std::string(format));
101+
if ((size_t)len >= buf.size()) {
102+
buf.resize(len+1); // extra null byte
103+
porting::mt_snprintf(&buf[0], buf.size(), format, std::forward<Args>(args)...);
104+
}
105+
buf.resize(len); // remove null bytes
106+
107+
return buf;
108+
}

src/unittest/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ set (UNITTEST_SRCS
3333
${CMAKE_CURRENT_SOURCE_DIR}/test_voxelarea.cpp
3434
${CMAKE_CURRENT_SOURCE_DIR}/test_voxelalgorithms.cpp
3535
${CMAKE_CURRENT_SOURCE_DIR}/test_voxelmanipulator.cpp
36+
${CMAKE_CURRENT_SOURCE_DIR}/test_gettext.cpp
3637
PARENT_SCOPE)
3738

3839
set (UNITTEST_CLIENT_SRCS

src/unittest/test_gettext.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#include "test.h"
2+
#include "porting.h"
3+
#include "gettext.h"
4+
5+
class TestGettext : public TestBase
6+
{
7+
public:
8+
TestGettext() {
9+
TestManager::registerTestModule(this);
10+
}
11+
12+
const char *getName() { return "TestGettext"; }
13+
14+
void runTests(IGameDef *gamedef);
15+
16+
void testSnfmtgettext();
17+
void testFmtgettext();
18+
};
19+
20+
static TestGettext g_test_instance;
21+
22+
void TestGettext::runTests(IGameDef *gamedef)
23+
{
24+
TEST(testFmtgettext);
25+
}
26+
27+
void TestGettext::testFmtgettext()
28+
{
29+
std::string buf = fmtgettext("Viewing range changed to %d", 12);
30+
UASSERTEQ(std::string, buf, "Viewing range changed to 12");
31+
buf = fmtgettext(
32+
"You are about to join this server with the name \"%s\" for the "
33+
"first time.\n"
34+
"If you proceed, a new account using your credentials will be "
35+
"created on this server.\n"
36+
"Please retype your password and click 'Register and Join' to "
37+
"confirm account creation, or click 'Cancel' to abort."
38+
, "A");
39+
UASSERTEQ(std::string, buf,
40+
"You are about to join this server with the name \"A\" for the "
41+
"first time.\n"
42+
"If you proceed, a new account using your credentials will be "
43+
"created on this server.\n"
44+
"Please retype your password and click 'Register and Join' to "
45+
"confirm account creation, or click 'Cancel' to abort."
46+
);
47+
}

util/updatepo.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ xgettext --package-name=minetest \
6161
--keyword=wstrgettext \
6262
--keyword=core.gettext \
6363
--keyword=showTranslatedStatusText \
64+
--keyword=fmtgettext \
6465
--output $potfile \
6566
--from-code=utf-8 \
6667
`find src/ -name '*.cpp' -o -name '*.h'` \

0 commit comments

Comments
 (0)