Browse Source

Translate builtin (#10693)

This PR is the second attempt to translate builtin.
Server-sent translation files can be added to `builtin/locale/`, whereas client-side translations depend on gettext.
Wuzzy 3 years ago
parent
commit
cafad6ac03

+ 1 - 2
.gitignore

@@ -86,8 +86,7 @@ src/test_config.h
 src/cmake_config.h
 src/cmake_config_githash.h
 src/unittest/test_world/world.mt
-src/lua/build/
-locale/
+/locale/
 .directory
 *.cbp
 *.layout

+ 4 - 5
builtin/client/chatcommands.lua

@@ -1,6 +1,5 @@
 -- Minetest: builtin/client/chatcommands.lua
 
-
 core.register_on_sending_chat_message(function(message)
 	if message:sub(1,2) == ".." then
 		return false
@@ -8,7 +7,7 @@ core.register_on_sending_chat_message(function(message)
 
 	local first_char = message:sub(1,1)
 	if first_char == "/" or first_char == "." then
-		core.display_chat_message(core.gettext("issued command: ") .. message)
+		core.display_chat_message(core.gettext("Issued command: ") .. message)
 	end
 
 	if first_char ~= "." then
@@ -19,7 +18,7 @@ core.register_on_sending_chat_message(function(message)
 	param = param or ""
 
 	if not cmd then
-		core.display_chat_message(core.gettext("-!- Empty command"))
+		core.display_chat_message("-!- " .. core.gettext("Empty command."))
 		return true
 	end
 
@@ -36,7 +35,7 @@ core.register_on_sending_chat_message(function(message)
 			core.display_chat_message(result)
 		end
 	else
-		core.display_chat_message(core.gettext("-!- Invalid command: ") .. cmd)
+		core.display_chat_message("-!- " .. core.gettext("Invalid command: ") .. cmd)
 	end
 
 	return true
@@ -66,7 +65,7 @@ core.register_chatcommand("clear_chat_queue", {
 	description = core.gettext("Clear the out chat queue"),
 	func = function(param)
 		core.clear_out_chat_queue()
-		return true, core.gettext("The out chat queue is now empty")
+		return true, core.gettext("The out chat queue is now empty.")
 	end,
 })
 

+ 1 - 1
builtin/client/death_formspec.lua

@@ -2,7 +2,7 @@
 -- handled by the engine.
 
 core.register_on_death(function()
-	core.display_chat_message("You died.")
+	core.display_chat_message(core.gettext("You died."))
 	local formspec = "size[11,5.5]bgcolor[#320000b4;true]" ..
 		"label[4.85,1.35;" .. fgettext("You died") ..
 		"]button_exit[4,3;3,0.5;btn_respawn;".. fgettext("Respawn") .."]"

+ 42 - 27
builtin/common/chatcommands.lua

@@ -1,5 +1,9 @@
 -- Minetest: builtin/common/chatcommands.lua
 
+-- For server-side translations (if INIT == "game")
+-- Otherwise, use core.gettext
+local S = core.get_translator("__builtin")
+
 core.registered_chatcommands = {}
 
 function core.register_chatcommand(cmd, def)
@@ -29,25 +33,12 @@ function core.override_chatcommand(name, redefinition)
 	core.registered_chatcommands[name] = chatcommand
 end
 
-local cmd_marker = "/"
-
-local function gettext(...)
-	return ...
-end
-
-local function gettext_replace(text, replace)
-	return text:gsub("$1", replace)
-end
-
-
-if INIT == "client" then
-	cmd_marker = "."
-	gettext = core.gettext
-	gettext_replace = fgettext_ne
-end
-
 local function do_help_cmd(name, param)
 	local function format_help_line(cmd, def)
+		local cmd_marker = "/"
+		if INIT == "client" then
+			cmd_marker = "."
+		end
 		local msg = core.colorize("#00ffff", cmd_marker .. cmd)
 		if def.params and def.params ~= "" then
 			msg = msg .. " " .. def.params
@@ -65,9 +56,21 @@ local function do_help_cmd(name, param)
 			end
 		end
 		table.sort(cmds)
-		return true, gettext("Available commands: ") .. table.concat(cmds, " ") .. "\n"
-				.. gettext_replace("Use '$1help <cmd>' to get more information,"
-				.. " or '$1help all' to list everything.", cmd_marker)
+		local msg
+		if INIT == "game" then
+			msg = S("Available commands: @1",
+				table.concat(cmds, " ")) .. "\n"
+				.. S("Use '/help <cmd>' to get more "
+				.. "information, or '/help all' to list "
+				.. "everything.")
+		else
+			msg = core.gettext("Available commands: ")
+				.. table.concat(cmds, " ") .. "\n"
+				.. core.gettext("Use '.help <cmd>' to get more "
+				.. "information, or '.help all' to list "
+				.. "everything.")
+		end
+		return true, msg
 	elseif param == "all" then
 		local cmds = {}
 		for cmd, def in pairs(core.registered_chatcommands) do
@@ -76,19 +79,31 @@ local function do_help_cmd(name, param)
 			end
 		end
 		table.sort(cmds)
-		return true, gettext("Available commands:").."\n"..table.concat(cmds, "\n")
+		local msg
+		if INIT == "game" then
+			msg = S("Available commands:")
+		else
+			msg = core.gettext("Available commands:")
+		end
+		return true, msg.."\n"..table.concat(cmds, "\n")
 	elseif INIT == "game" and param == "privs" then
 		local privs = {}
 		for priv, def in pairs(core.registered_privileges) do
 			privs[#privs + 1] = priv .. ": " .. def.description
 		end
 		table.sort(privs)
-		return true, "Available privileges:\n"..table.concat(privs, "\n")
+		return true, S("Available privileges:").."\n"..table.concat(privs, "\n")
 	else
 		local cmd = param
 		local def = core.registered_chatcommands[cmd]
 		if not def then
-			return false, gettext("Command not available: ")..cmd
+			local msg
+			if INIT == "game" then
+				msg = S("Command not available: @1", cmd)
+			else
+				msg = core.gettext("Command not available: ") .. cmd
+			end
+			return false, msg
 		else
 			return true, format_help_line(cmd, def)
 		end
@@ -97,16 +112,16 @@ end
 
 if INIT == "client" then
 	core.register_chatcommand("help", {
-		params = gettext("[all | <cmd>]"),
-		description = gettext("Get help for commands"),
+		params = core.gettext("[all | <cmd>]"),
+		description = core.gettext("Get help for commands"),
 		func = function(param)
 			return do_help_cmd(nil, param)
 		end,
 	})
 else
 	core.register_chatcommand("help", {
-		params = "[all | privs | <cmd>]",
-		description = "Get help for commands or list privileges",
+		params = S("[all | privs | <cmd>]"),
+		description = S("Get help for commands or list privileges"),
 		func = do_help_cmd,
 	})
 end

+ 15 - 13
builtin/common/information_formspecs.lua

@@ -20,7 +20,8 @@ local LIST_FORMSPEC_DESCRIPTION = [[
 		button_exit[5,7;3,1;quit;%s]
 	]]
 
-local formspec_escape = core.formspec_escape
+local F = core.formspec_escape
+local S = core.get_translator("__builtin")
 local check_player_privs = core.check_player_privs
 
 
@@ -51,22 +52,23 @@ core.after(0, load_mod_command_tree)
 
 local function build_chatcommands_formspec(name, sel, copy)
 	local rows = {}
-	rows[1] = "#FFF,0,Command,Parameters"
+	rows[1] = "#FFF,0,"..F(S("Command"))..","..F(S("Parameters"))
 
-	local description = "For more information, click on any entry in the list.\n" ..
-		"Double-click to copy the entry to the chat history."
+	local description = S("For more information, click on "
+		.. "any entry in the list.").. "\n" ..
+		S("Double-click to copy the entry to the chat history.")
 
 	for i, data in ipairs(mod_cmds) do
-		rows[#rows + 1] = COLOR_BLUE .. ",0," .. formspec_escape(data[1]) .. ","
+		rows[#rows + 1] = COLOR_BLUE .. ",0," .. F(data[1]) .. ","
 		for j, cmds in ipairs(data[2]) do
 			local has_priv = check_player_privs(name, cmds[2].privs)
 			rows[#rows + 1] = ("%s,1,%s,%s"):format(
 				has_priv and COLOR_GREEN or COLOR_GRAY,
-				cmds[1], formspec_escape(cmds[2].params))
+				cmds[1], F(cmds[2].params))
 			if sel == #rows then
 				description = cmds[2].description
 				if copy then
-					core.chat_send_player(name, ("Command: %s %s"):format(
+					core.chat_send_player(name, S("Command: @1 @2",
 						core.colorize("#0FF", "/" .. cmds[1]), cmds[2].params))
 				end
 			end
@@ -74,9 +76,9 @@ local function build_chatcommands_formspec(name, sel, copy)
 	end
 
 	return LIST_FORMSPEC_DESCRIPTION:format(
-			"Available commands: (see also: /help <cmd>)",
+			F(S("Available commands: (see also: /help <cmd>)")),
 			table.concat(rows, ","), sel or 0,
-			description, "Close"
+			F(description), F(S("Close"))
 		)
 end
 
@@ -91,19 +93,19 @@ local function build_privs_formspec(name)
 	table.sort(privs, function(a, b) return a[1] < b[1] end)
 
 	local rows = {}
-	rows[1] = "#FFF,0,Privilege,Description"
+	rows[1] = "#FFF,0,"..F(S("Privilege"))..","..F(S("Description"))
 
 	local player_privs = core.get_player_privs(name)
 	for i, data in ipairs(privs) do
 		rows[#rows + 1] = ("%s,0,%s,%s"):format(
 			player_privs[data[1]] and COLOR_GREEN or COLOR_GRAY,
-				data[1], formspec_escape(data[2].description))
+				data[1], F(data[2].description))
 	end
 
 	return LIST_FORMSPEC:format(
-			"Available privileges:",
+			F(S("Available privileges:")),
 			table.concat(rows, ","),
-			"Close"
+			F(S("Close"))
 		)
 end
 

File diff suppressed because it is too large
+ 246 - 202
builtin/game/chat.lua


+ 21 - 19
builtin/game/privileges.lua

@@ -1,5 +1,7 @@
 -- Minetest: builtin/privileges.lua
 
+local S = core.get_translator("__builtin")
+
 --
 -- Privileges
 --
@@ -15,7 +17,7 @@ function core.register_privilege(name, param)
 			def.give_to_admin = def.give_to_singleplayer
 		end
 		if def.description == nil then
-			def.description = "(no description)"
+			def.description = S("(no description)")
 		end
 	end
 	local def
@@ -28,69 +30,69 @@ function core.register_privilege(name, param)
 	core.registered_privileges[name] = def
 end
 
-core.register_privilege("interact", "Can interact with things and modify the world")
-core.register_privilege("shout", "Can speak in chat")
-core.register_privilege("basic_privs", "Can modify 'shout' and 'interact' privileges")
-core.register_privilege("privs", "Can modify privileges")
+core.register_privilege("interact", S("Can interact with things and modify the world"))
+core.register_privilege("shout", S("Can speak in chat"))
+core.register_privilege("basic_privs", S("Can modify 'shout' and 'interact' privileges"))
+core.register_privilege("privs", S("Can modify privileges"))
 
 core.register_privilege("teleport", {
-	description = "Can teleport self",
+	description = S("Can teleport self"),
 	give_to_singleplayer = false,
 })
 core.register_privilege("bring", {
-	description = "Can teleport other players",
+	description = S("Can teleport other players"),
 	give_to_singleplayer = false,
 })
 core.register_privilege("settime", {
-	description = "Can set the time of day using /time",
+	description = S("Can set the time of day using /time"),
 	give_to_singleplayer = false,
 })
 core.register_privilege("server", {
-	description = "Can do server maintenance stuff",
+	description = S("Can do server maintenance stuff"),
 	give_to_singleplayer = false,
 	give_to_admin = true,
 })
 core.register_privilege("protection_bypass", {
-	description = "Can bypass node protection in the world",
+	description = S("Can bypass node protection in the world"),
 	give_to_singleplayer = false,
 })
 core.register_privilege("ban", {
-	description = "Can ban and unban players",
+	description = S("Can ban and unban players"),
 	give_to_singleplayer = false,
 	give_to_admin = true,
 })
 core.register_privilege("kick", {
-	description = "Can kick players",
+	description = S("Can kick players"),
 	give_to_singleplayer = false,
 	give_to_admin = true,
 })
 core.register_privilege("give", {
-	description = "Can use /give and /giveme",
+	description = S("Can use /give and /giveme"),
 	give_to_singleplayer = false,
 })
 core.register_privilege("password", {
-	description = "Can use /setpassword and /clearpassword",
+	description = S("Can use /setpassword and /clearpassword"),
 	give_to_singleplayer = false,
 	give_to_admin = true,
 })
 core.register_privilege("fly", {
-	description = "Can use fly mode",
+	description = S("Can use fly mode"),
 	give_to_singleplayer = false,
 })
 core.register_privilege("fast", {
-	description = "Can use fast mode",
+	description = S("Can use fast mode"),
 	give_to_singleplayer = false,
 })
 core.register_privilege("noclip", {
-	description = "Can fly through solid nodes using noclip mode",
+	description = S("Can fly through solid nodes using noclip mode"),
 	give_to_singleplayer = false,
 })
 core.register_privilege("rollback", {
-	description = "Can use the rollback functionality",
+	description = S("Can use the rollback functionality"),
 	give_to_singleplayer = false,
 })
 core.register_privilege("debug", {
-	description = "Allows enabling various debug options that may affect gameplay",
+	description = S("Allows enabling various debug options that may affect gameplay"),
 	give_to_singleplayer = false,
 	give_to_admin = true,
 })

+ 6 - 4
builtin/game/register.lua

@@ -1,5 +1,7 @@
 -- Minetest: builtin/misc_register.lua
 
+local S = core.get_translator("__builtin")
+
 --
 -- Make raw registration functions inaccessible to anyone except this file
 --
@@ -326,7 +328,7 @@ end
 
 core.register_item(":unknown", {
 	type = "none",
-	description = "Unknown Item",
+	description = S("Unknown Item"),
 	inventory_image = "unknown_item.png",
 	on_place = core.item_place,
 	on_secondary_use = core.item_secondary_use,
@@ -336,7 +338,7 @@ core.register_item(":unknown", {
 })
 
 core.register_node(":air", {
-	description = "Air",
+	description = S("Air"),
 	inventory_image = "air.png",
 	wield_image = "air.png",
 	drawtype = "airlike",
@@ -353,7 +355,7 @@ core.register_node(":air", {
 })
 
 core.register_node(":ignore", {
-	description = "Ignore",
+	description = S("Ignore"),
 	inventory_image = "ignore.png",
 	wield_image = "ignore.png",
 	drawtype = "airlike",
@@ -370,7 +372,7 @@ core.register_node(":ignore", {
 		core.chat_send_player(
 				placer:get_player_name(),
 				core.colorize("#FF0000",
-				"You can't place 'ignore' nodes!"))
+				S("You can't place 'ignore' nodes!")))
 		return ""
 	end,
 })

+ 224 - 0
builtin/locale/template.txt

@@ -0,0 +1,224 @@
+# textdomain: __builtin
+Empty command.=
+Invalid command: @1=
+Invalid command usage.=
+You don't have permission to run this command (missing privileges: @1).=
+Unable to get position of player @1.=
+Incorrect area format. Expected: (x1,y1,z1) (x2,y2,z2)=
+<action>=
+Show chat action (e.g., '/me orders a pizza' displays '<player name> orders a pizza')=
+Show the name of the server owner=
+The administrator of this server is @1.=
+There's no administrator named in the config file.=
+[<name>]=
+Show privileges of yourself or another player=
+Player @1 does not exist.=
+Privileges of @1: @2=
+<privilege>=
+Return list of all online players with privilege=
+Invalid parameters (see /help haspriv).=
+Unknown privilege!=
+Players online with the "@1" privilege: @2=
+Your privileges are insufficient.=
+Unknown privilege: @1=
+@1 granted you privileges: @2=
+<name> (<privilege> | all)=
+Give privileges to player=
+Invalid parameters (see /help grant).=
+<privilege> | all=
+Grant privileges to yourself=
+Invalid parameters (see /help grantme).=
+@1 revoked privileges from you: @2=
+Remove privileges from player=
+Invalid parameters (see /help revoke).=
+Revoke privileges from yourself=
+Invalid parameters (see /help revokeme).=
+<name> <password>=
+Set player's password=
+Name field required.=
+Your password was cleared by @1.=
+Password of player "@1" cleared.=
+Your password was set by @1.=
+Password of player "@1" set.=
+<name>=
+Set empty password for a player=
+Reload authentication data=
+Done.=
+Failed.=
+Remove a player's data=
+Player "@1" removed.=
+No such player "@1" to remove.=
+Player "@1" is connected, cannot remove.=
+Unhandled remove_player return code @1.=
+Cannot teleport out of map bounds!=
+Cannot get player with name @1.=
+Cannot teleport, @1 is attached to an object!=
+Teleporting @1 to @2.=
+One does not teleport to oneself.=
+Cannot get teleportee with name @1.=
+Cannot get target player with name @1.=
+Teleporting @1 to @2 at @3.=
+<X>,<Y>,<Z> | <to_name> | <name> <X>,<Y>,<Z> | <name> <to_name>=
+Teleport to position or player=
+You don't have permission to teleport other players (missing privilege: @1).=
+([-n] <name> <value>) | <name>=
+Set or read server configuration setting=
+Failed. Use '/set -n <name> <value>' to create a new setting.=
+@1 @= @2=
+<not set>=
+Invalid parameters (see /help set).=
+Finished emerging @1 blocks in @2ms.=
+emergeblocks update: @1/@2 blocks emerged (@3%)=
+(here [<radius>]) | (<pos1> <pos2>)=
+Load (or, if nonexistent, generate) map blocks contained in area pos1 to pos2 (<pos1> and <pos2> must be in parentheses)=
+Started emerge of area ranging from @1 to @2.=
+Delete map blocks contained in area pos1 to pos2 (<pos1> and <pos2> must be in parentheses)=
+Successfully cleared area ranging from @1 to @2.=
+Failed to clear one or more blocks in area.=
+Resets lighting in the area between pos1 and pos2 (<pos1> and <pos2> must be in parentheses)=
+Successfully reset light in the area ranging from @1 to @2.=
+Failed to load one or more blocks in area.=
+List mods installed on the server=
+Cannot give an empty item.=
+Cannot give an unknown item.=
+Giving 'ignore' is not allowed.=
+@1 is not a known player.=
+@1 partially added to inventory.=
+@1 could not be added to inventory.=
+@1 added to inventory.=
+@1 partially added to inventory of @2.=
+@1 could not be added to inventory of @2.=
+@1 added to inventory of @2.=
+<name> <ItemString> [<count> [<wear>]]=
+Give item to player=
+Name and ItemString required.=
+<ItemString> [<count> [<wear>]]=
+Give item to yourself=
+ItemString required.=
+<EntityName> [<X>,<Y>,<Z>]=
+Spawn entity at given (or your) position=
+EntityName required.=
+Unable to spawn entity, player is nil.=
+Cannot spawn an unknown entity.=
+Invalid parameters (@1).=
+@1 spawned.=
+@1 failed to spawn.=
+Destroy item in hand=
+Unable to pulverize, no player.=
+Unable to pulverize, no item in hand.=
+An item was pulverized.=
+[<range>] [<seconds>] [<limit>]=
+Check who last touched a node or a node near it within the time specified by <seconds>. Default: range @= 0, seconds @= 86400 @= 24h, limit @= 5. Set <seconds> to inf for no time limit=
+Rollback functions are disabled.=
+That limit is too high!=
+Checking @1 ...=
+Nobody has touched the specified location in @1 seconds.=
+@1 @2 @3 -> @4 @5 seconds ago.=
+Punch a node (range@=@1, seconds@=@2, limit@=@3).=
+(<name> [<seconds>]) | (:<actor> [<seconds>])=
+Revert actions of a player. Default for <seconds> is 60. Set <seconds> to inf for no time limit=
+Invalid parameters. See /help rollback and /help rollback_check.=
+Reverting actions of player '@1' since @2 seconds.=
+Reverting actions of @1 since @2 seconds.=
+(log is too long to show)=
+Reverting actions succeeded.=
+Reverting actions FAILED.=
+Show server status=
+This command was disabled by a mod or game.=
+[<0..23>:<0..59> | <0..24000>]=
+Show or set time of day=
+Current time is @1:@2.=
+You don't have permission to run this command (missing privilege: @1).=
+Invalid time.=
+Time of day changed.=
+Invalid hour (must be between 0 and 23 inclusive).=
+Invalid minute (must be between 0 and 59 inclusive).=
+Show day count since world creation=
+Current day is @1.=
+[<delay_in_seconds> | -1] [reconnect] [<message>]=
+Shutdown server (-1 cancels a delayed shutdown)=
+Server shutting down (operator request).=
+Ban the IP of a player or show the ban list=
+The ban list is empty.=
+Ban list: @1=
+Player is not online.=
+Failed to ban player.=
+Banned @1.=
+<name> | <IP_address>=
+Remove IP ban belonging to a player/IP=
+Failed to unban player/IP.=
+Unbanned @1.=
+<name> [<reason>]=
+Kick a player=
+Failed to kick player @1.=
+Kicked @1.=
+[full | quick]=
+Clear all objects in world=
+Invalid usage, see /help clearobjects.=
+Clearing all objects. This may take a long time. You may experience a timeout. (by @1)=
+Cleared all objects.=
+<name> <message>=
+Send a direct message to a player=
+Invalid usage, see /help msg.=
+The player @1 is not online.=
+DM from @1: @2=
+Message sent.=
+Get the last login time of a player or yourself=
+@1's last login time was @2.=
+@1's last login time is unknown.=
+Clear the inventory of yourself or another player=
+You don't have permission to clear another player's inventory (missing privilege: @1).=
+@1 cleared your inventory.=
+Cleared @1's inventory.=
+Player must be online to clear inventory!=
+Players can't be killed, damage has been disabled.=
+Player @1 is not online.=
+You are already dead.=
+@1 is already dead.=
+@1 has been killed.=
+Kill player or yourself=
+Available commands: @1=
+Use '/help <cmd>' to get more information, or '/help all' to list everything.=
+Available commands:=
+Command not available: @1=
+[all | privs | <cmd>]=
+Get help for commands or list privileges=
+Available privileges:=
+Command=
+Parameters=
+For more information, click on any entry in the list.=
+Double-click to copy the entry to the chat history.=
+Command: @1 @2=
+Available commands: (see also: /help <cmd>)=
+Close=
+Privilege=
+Description=
+print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=
+Handle the profiler and profiling data=
+Statistics written to action log.=
+Statistics were reset.=
+Usage: @1=
+Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=
+(no description)=
+Can interact with things and modify the world=
+Can speak in chat=
+Can modify 'shout' and 'interact' privileges=
+Can modify privileges=
+Can teleport self=
+Can teleport other players=
+Can set the time of day using /time=
+Can do server maintenance stuff=
+Can bypass node protection in the world=
+Can ban and unban players=
+Can kick players=
+Can use /give and /giveme=
+Can use /setpassword and /clearpassword=
+Can use fly mode=
+Can use fast mode=
+Can fly through solid nodes using noclip mode=
+Can use the rollback functionality=
+Allows enabling various debug options that may affect gameplay=
+Unknown Item=
+Air=
+Ignore=
+You can't place 'ignore' nodes!=

+ 9 - 9
builtin/profiler/init.lua

@@ -15,6 +15,8 @@
 --with this program; if not, write to the Free Software Foundation, Inc.,
 --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
+local S = core.get_translator("__builtin")
+
 local function get_bool_default(name, default)
 	local val = core.settings:get_bool(name)
 	if val == nil then
@@ -40,9 +42,9 @@ function profiler.init_chatcommand()
 		instrumentation.init_chatcommand()
 	end
 
-	local param_usage = "print [filter] | dump [filter] | save [format [filter]] | reset"
+	local param_usage = S("print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset")
 	core.register_chatcommand("profiler", {
-		description = "handle the profiler and profiling data",
+		description = S("Handle the profiler and profiling data"),
 		params = param_usage,
 		privs = { server=true },
 		func = function(name, param)
@@ -51,21 +53,19 @@ function profiler.init_chatcommand()
 
 			if command == "dump" then
 				core.log("action", reporter.print(sampler.profile, arg0))
-				return true, "Statistics written to action log"
+				return true, S("Statistics written to action log.")
 			elseif command == "print" then
 				return true, reporter.print(sampler.profile, arg0)
 			elseif command == "save" then
 				return reporter.save(sampler.profile, args[1] or "txt", args[2])
 			elseif command == "reset" then
 				sampler.reset()
-				return true, "Statistics were reset"
+				return true, S("Statistics were reset.")
 			end
 
-			return false, string.format(
-				"Usage: %s\n" ..
-				"Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).",
-				param_usage
-			)
+			return false,
+				S("Usage: @1", param_usage) .. "\n" ..
+				S("Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).")
 		end
 	})
 

+ 3 - 1
src/server.cpp

@@ -2496,7 +2496,9 @@ void Server::fillMediaCache()
 
 	// Collect all media file paths
 	std::vector<std::string> paths;
-	// The paths are ordered in descending priority
+
+	// ordered in descending priority
+	paths.push_back(getBuiltinLuaPath() + DIR_DELIM + "locale");
 	fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
 	fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
 	m_modmgr->getModsMediaPaths(paths);

+ 1 - 0
util/updatepo.sh

@@ -58,6 +58,7 @@ xgettext --package-name=minetest \
 	--keyword=fgettext_ne \
 	--keyword=strgettext \
 	--keyword=wstrgettext \
+	--keyword=core.gettext \
 	--keyword=showTranslatedStatusText \
 	--output $potfile \
 	--from-code=utf-8 \

Some files were not shown because too many files changed in this diff