update castles, areas, bakedclay, basic_materials, currency, digistuff,

farming_redo, homedecor, street_signs, technic, and unified inventory

removed item_tweaks and teleport_request
This commit is contained in:
Vanessa Dannenberg 2020-04-05 22:04:00 -04:00
parent e6f3313f30
commit a8ed4410a6
380 changed files with 3305 additions and 4304 deletions

View File

@ -2,8 +2,8 @@
# ./init.lua
@1 cannot be repaired with an anvil.=
@1's anvil=
@1 cannot be repaired with an anvil.=@1 ne peut pas être réparé avec une enclume.
@1's anvil=enclume de @1
A tool for repairing other tools at a blacksmith's anvil.=Un outil pour réparer les autres outils avec une enclume de forgeron.
A tool for repairing other tools in conjunction with a blacksmith's hammer.=Un outil pour réparer les autres outils à utiliser avec un marteau de forgeron.
Anvil=Enclume
@ -11,4 +11,4 @@ Right-click on this anvil with a damaged tool to place the damaged tool upon it.
Steel blacksmithing hammer=Marteau de forgeron en acier
This anvil is for damaged tools only.=L'enclume s'utilise sur les outils endommagés.
Use this hammer to strike blows upon an anvil bearing a damaged tool and you can repair it. It can also be used for smashing stone, but it is not well suited to this task.=Utilisez ce marteau pour frapper une enclume contenant un outil endommagé, ainsi vous pourrez le réparer. Il peut être aussi utilisé pour casser de la pierre, mais il n'est pas adapté à cette tâche.
Your @1 has been repaired successfully.=Votre @1 a été réparé avec succès.
Your @1 has been repaired successfully.=Votre @1 a été réparé avec succès.

View File

@ -9,7 +9,6 @@ read_globals = {
"VoxelManip", "VoxelArea",
"PseudoRandom", "ItemStack",
"AreaStore",
"intllib",
"default",
table = { fields = { "copy", "getn" } }
}

View File

@ -1,15 +1,16 @@
local S = minetest.get_translator("areas")
minetest.register_chatcommand("protect", {
params = "<AreaName>",
description = "Protect your own area",
params = S("<AreaName>"),
description = S("Protect your own area"),
privs = {[areas.config.self_protection_privilege]=true},
func = function(name, param)
if param == "" then
return false, "Invalid usage, see /help protect."
return false, S("Invalid usage, see /help @1.", "protect")
end
local pos1, pos2 = areas:getPos(name)
if not (pos1 and pos2) then
return false, "You need to select an area first."
return false, S("You need to select an area first.")
end
minetest.log("action", "/protect invoked, owner="..name..
@ -19,38 +20,37 @@ minetest.register_chatcommand("protect", {
local canAdd, errMsg = areas:canPlayerAddArea(pos1, pos2, name)
if not canAdd then
return false, "You can't protect that area: "..errMsg
return false, S("You can't protect that area: @1", errMsg)
end
local id = areas:add(name, param, pos1, pos2, nil)
areas:save()
return true, "Area protected. ID: "..id
return true, S("Area protected. ID: @1", id)
end
})
minetest.register_chatcommand("set_owner", {
params = "<PlayerName> <AreaName>",
description = "Protect an area beetween two positions and give"
params = S("<PlayerName>").." "..S("<AreaName>"),
description = S("Protect an area between two positions and give"
.." a player access to it without setting the parent of the"
.." area to any existing area",
.." area to any existing area"),
privs = areas.adminPrivs,
func = function(name, param)
local ownerName, areaName = param:match('^(%S+)%s(.+)$')
if not ownerName then
return false, "Incorrect usage, see /help set_owner."
return false, S("Invalid usage, see /help @1.", "set_owner")
end
local pos1, pos2 = areas:getPos(name)
if not (pos1 and pos2) then
return false, "You need to select an area first."
return false, S("You need to select an area first.")
end
if not areas:player_exists(ownerName) then
return false, "The player \""
..ownerName.."\" does not exist."
return false, S("The player \"@1\" does not exist.", ownerName)
end
minetest.log("action", name.." runs /set_owner. Owner = "..ownerName..
@ -62,34 +62,34 @@ minetest.register_chatcommand("set_owner", {
areas:save()
minetest.chat_send_player(ownerName,
"You have been granted control over area #"..
id..". Type /list_areas to show your areas.")
return true, "Area protected. ID: "..id
S("You have been granted control over area #@1. "..
"Type /list_areas to show your areas.", id))
return true, S("Area protected. ID: @1", id)
end
})
minetest.register_chatcommand("add_owner", {
params = "<ParentID> <Player> <AreaName>",
description = "Give a player access to a sub-area beetween two"
params = S("<ParentID>").." "..S("<PlayerName>").." "..S("<AreaName>"),
description = S("Give a player access to a sub-area beetween two"
.." positions that have already been protected,"
.." Use set_owner if you don't want the parent to be set.",
.." Use set_owner if you don't want the parent to be set."),
func = function(name, param)
local pid, ownerName, areaName
= param:match('^(%d+) ([^ ]+) (.+)$')
if not pid then
minetest.chat_send_player(name, "Incorrect usage, see /help add_owner")
minetest.chat_send_player(name, S("Invalid usage, see /help @1.", "add_owner"))
return
end
local pos1, pos2 = areas:getPos(name)
if not (pos1 and pos2) then
return false, "You need to select an area first."
return false, S("You need to select an area first.")
end
if not areas:player_exists(ownerName) then
return false, "The player \""..ownerName.."\" does not exist."
return false, S("The player \"@1\" does not exist.", ownerName)
end
minetest.log("action", name.." runs /add_owner. Owner = "..ownerName..
@ -101,52 +101,52 @@ minetest.register_chatcommand("add_owner", {
pid = tonumber(pid)
if (not areas:isAreaOwner(pid, name)) or
(not areas:isSubarea(pos1, pos2, pid)) then
return false, "You can't protect that area."
return false, S("You can't protect that area.")
end
local id = areas:add(ownerName, areaName, pos1, pos2, pid)
areas:save()
minetest.chat_send_player(ownerName,
"You have been granted control over area #"..
id..". Type /list_areas to show your areas.")
return true, "Area protected. ID: "..id
S("You have been granted control over area #@1. "..
"Type /list_areas to show your areas.", id))
return true, S("Area protected. ID: @1", id)
end
})
minetest.register_chatcommand("rename_area", {
params = "<ID> <newName>",
description = "Rename a area that you own",
params = S("<ID>").." "..S("<newName>"),
description = S("Rename an area that you own"),
func = function(name, param)
local id, newName = param:match("^(%d+)%s(.+)$")
if not id then
return false, "Invalid usage, see /help rename_area."
return false, S("Invalid usage, see /help @1.", "rename_area")
end
id = tonumber(id)
if not id then
return false, "That area doesn't exist."
return false, S("That area doesn't exist.")
end
if not areas:isAreaOwner(id, name) then
return true, "You don't own that area."
return true, S("You don't own that area.")
end
areas.areas[id].name = newName
areas:save()
return true, "Area renamed."
return true, S("Area renamed.")
end
})
minetest.register_chatcommand("find_areas", {
params = "<regexp>",
description = "Find areas using a Lua regular expression",
description = S("Find areas using a Lua regular expression"),
privs = areas.adminPrivs,
func = function(name, param)
if param == "" then
return false, "A regular expression is required."
return false, S("A regular expression is required.")
end
-- Check expression for validity
@ -154,7 +154,7 @@ minetest.register_chatcommand("find_areas", {
("Test [1]: Player (0,0,0) (0,0,0)"):find(param)
end
if not pcall(testRegExp) then
return false, "Invalid regular expression."
return false, S("Invalid regular expression.")
end
local matches = {}
@ -167,14 +167,14 @@ minetest.register_chatcommand("find_areas", {
if #matches > 0 then
return true, table.concat(matches, "\n")
else
return true, "No matches found."
return true, S("No matches found.")
end
end
})
minetest.register_chatcommand("list_areas", {
description = "List your areas, or all areas if you are an admin.",
description = S("List your areas, or all areas if you are an admin."),
func = function(name, param)
local admin = minetest.check_player_privs(name, areas.adminPrivs)
local areaStrings = {}
@ -184,7 +184,7 @@ minetest.register_chatcommand("list_areas", {
end
end
if #areaStrings == 0 then
return true, "No visible areas."
return true, S("No visible areas.")
end
return true, table.concat(areaStrings, "\n")
end
@ -192,154 +192,154 @@ minetest.register_chatcommand("list_areas", {
minetest.register_chatcommand("recursive_remove_areas", {
params = "<id>",
description = "Recursively remove areas using an id",
params = S("<ID>"),
description = S("Recursively remove areas using an ID"),
func = function(name, param)
local id = tonumber(param)
if not id then
return false, "Invalid usage, see"
.." /help recursive_remove_areas"
return false, S("Invalid usage, see"
.." /help @1.", "recursive_remove_areas")
end
if not areas:isAreaOwner(id, name) then
return false, "Area "..id.." does not exist or is"
.." not owned by you."
return false, S("Area @1 does not exist or is"
.." not owned by you.", id)
end
areas:remove(id, true)
areas:save()
return true, "Removed area "..id.." and it's sub areas."
return true, S("Removed area @1 and it's sub areas.", id)
end
})
minetest.register_chatcommand("remove_area", {
params = "<id>",
description = "Remove an area using an id",
params = S("<ID>"),
description = S("Remove an area using an ID"),
func = function(name, param)
local id = tonumber(param)
if not id then
return false, "Invalid usage, see /help remove_area"
return false, S("Invalid usage, see /help @1.", "remove_area")
end
if not areas:isAreaOwner(id, name) then
return false, "Area "..id.." does not exist or"
.." is not owned by you."
return false, S("Area @1 does not exist or"
.." is not owned by you.", id)
end
areas:remove(id)
areas:save()
return true, "Removed area "..id
return true, S("Removed area @1", id)
end
})
minetest.register_chatcommand("change_owner", {
params = "<ID> <NewOwner>",
description = "Change the owner of an area using it's ID",
params = S("<ID>").." "..S("<NewOwner>"),
description = S("Change the owner of an area using its ID"),
func = function(name, param)
local id, newOwner = param:match("^(%d+)%s(%S+)$")
if not id then
return false, "Invalid usage, see"
.." /help change_owner."
return false, S("Invalid usage, see"
.." /help @1.", "change_owner")
end
if not areas:player_exists(newOwner) then
return false, "The player \""..newOwner
.."\" does not exist."
return false, S("The player \"@1\" does not exist.", newOwner)
end
id = tonumber(id)
if not areas:isAreaOwner(id, name) then
return false, "Area "..id.." does not exist"
.." or is not owned by you."
return false, S("Area @1 does not exist"
.." or is not owned by you.", id)
end
areas.areas[id].owner = newOwner
areas:save()
minetest.chat_send_player(newOwner,
("%s has given you control over the area %q (ID %d).")
:format(name, areas.areas[id].name, id))
return true, "Owner changed."
S("@1 has given you control over the area \"@2\" (ID @3).",
name, areas.areas[id].name, id))
return true, S("Owner changed.")
end
})
minetest.register_chatcommand("area_open", {
params = "<ID>",
description = "Toggle an area open (anyone can interact) or closed",
params = S("<ID>"),
description = S("Toggle an area open (anyone can interact) or closed"),
func = function(name, param)
local id = tonumber(param)
if not id then
return false, "Invalid usage, see /help area_open."
return false, S("Invalid usage, see /help @1.", "area_open")
end
if not areas:isAreaOwner(id, name) then
return false, "Area "..id.." does not exist"
.." or is not owned by you."
return false, S("Area @1 does not exist"
.." or is not owned by you.", id)
end
local open = not areas.areas[id].open
-- Save false as nil to avoid inflating the DB.
areas.areas[id].open = open or nil
areas:save()
return true, ("Area %s."):format(open and "opened" or "closed")
return true, open and S("Area opened.") or S("Area closed.")
end
})
if areas.factions_available then
minetest.register_chatcommand("area_faction_open", {
params = "<ID>",
description = "Toggle an area open/closed for members in your faction.",
params = S("<ID>"),
description = S("Toggle an area open/closed for members in your faction."),
func = function(name, param)
local id = tonumber(param)
if not id then
return false, "Invalid usage, see /help area_faction_open."
return false, S("Invalid usage, see /help @1.", "area_faction_open")
end
if not areas:isAreaOwner(id, name) then
return false, "Area "..id.." does not exist"
.." or is not owned by you."
return false, S("Area @1 does not exist"
.." or is not owned by you.", id)
end
local open = not areas.areas[id].faction_open
-- Save false as nil to avoid inflating the DB.
areas.areas[id].faction_open = open or nil
areas:save()
return true, ("Area %s for faction members."):format(open and "opened" or "closed")
return true, open and S("Area opened for faction members.")
or S("Area closed for faction members.")
end
})
end
minetest.register_chatcommand("move_area", {
params = "<ID>",
description = "Move (or resize) an area to the current positions.",
params = S("<ID>"),
description = S("Move (or resize) an area to the current positions."),
privs = areas.adminPrivs,
func = function(name, param)
local id = tonumber(param)
if not id then
return false, "Invalid usage, see /help move_area."
return false, S("Invalid usage, see /help @1.", "move_area")
end
local area = areas.areas[id]
if not area then
return false, "Area does not exist."
return false, S("Area does not exist.")
end
local pos1, pos2 = areas:getPos(name)
if not pos1 then
return false, "You need to select an area first."
return false, S("You need to select an area first.")
end
areas:move(id, area, pos1, pos2)
areas:save()
return true, "Area successfully moved."
return true, S("Area successfully moved.")
end,
})
minetest.register_chatcommand("area_info", {
description = "Get information about area configuration and usage.",
description = S("Get information about area configuration and usage."),
func = function(name, param)
local lines = {}
local privs = minetest.get_player_privs(name)
@ -361,27 +361,22 @@ minetest.register_chatcommand("area_info", {
local max_size = has_high_limit and
size_limit_high or size_limit
-- Privilege information
local self_prot_line = ("Self protection is %sabled"):format(
self_prot and "en" or "dis")
if self_prot and prot_priv then
self_prot_line = self_prot_line..
(" %s have the neccessary privilege (%q).")
:format(
has_prot_priv and "and you" or
"but you don't",
prot_priv)
else
self_prot_line = self_prot_line.."."
end
-- Self protection information
local self_prot_line = self_prot and S("Self protection is enabled.") or
S("Self protection is disabled.")
table.insert(lines, self_prot_line)
-- Privilege information
local priv_line = has_prot_priv and
S("You have the necessary privilege (\"@1\").", prot_priv) or
S("You don't have the necessary privilege (\"@1\").", prot_priv)
table.insert(lines, priv_line)
if privs.areas then
table.insert(lines, "You are an area"..
" administrator (\"areas\" privilege).")
table.insert(lines, S("You are an area"..
" administrator (\"areas\" privilege)."))
elseif has_high_limit then
table.insert(lines,
"You have extended area protection"..
" limits (\"areas_high_limit\" privilege).")
S("You have extended area protection"..
" limits (\"areas_high_limit\" privilege)."))
end
-- Area count
@ -391,26 +386,23 @@ minetest.register_chatcommand("area_info", {
area_num = area_num + 1
end
end
local count_line = ("You have %d area%s"):format(
area_num, area_num == 1 and "" or "s")
if privs.areas then
count_line = count_line..
" and have no area protection limits."
elseif can_prot then
count_line = count_line..(", out of a maximum of %d.")
:format(max_count)
end
table.insert(lines, count_line)
table.insert(lines, S("You have @1 areas.", area_num))
-- Area limit
local area_limit_line = privs.areas and
S("Limit: no area count limit") or
S("Limit: @1 areas", max_count)
table.insert(lines, area_limit_line)
-- Area size limits
local function size_info(str, size)
table.insert(lines, ("%s spanning up to %dx%dx%d.")
:format(str, size.x, size.y, size.z))
table.insert(lines, S("@1 spanning up to @2x@3x@4.",
str, size.x, size.y, size.z))
end
local function priv_limit_info(lpriv, lmax_count, lmax_size)
size_info(("Players with the %q privilege"..
" can protect up to %d areas"):format(
lpriv, lmax_count), lmax_size)
size_info(S("Players with the \"@1\" privilege"..
" can protect up to @2 areas", lpriv, lmax_count),
lmax_size)
end
if self_prot then
if privs.areas then
@ -419,7 +411,7 @@ minetest.register_chatcommand("area_info", {
priv_limit_info("areas_high_limit",
limit_high, size_limit_high)
elseif has_prot_priv then
size_info("You can protect areas", max_size)
size_info(S("You can protect areas"), max_size)
end
end

View File

@ -1,5 +1,5 @@
-- This is inspired by the landrush mod by Bremaweb
local S = minetest.get_translator("areas")
areas.hud = {}
areas.hud.refresh = 0
@ -26,7 +26,7 @@ minetest.register_globalstep(function(dtime)
area.faction_open = faction_info
table.insert(areaStrings, ("%s [%u] (%s%s%s)")
:format(area.name, id, area.owner,
area.open and ":open" or "",
area.open and S(":open") or "",
faction_info and ":"..faction_info or ""))
end
@ -38,7 +38,7 @@ minetest.register_globalstep(function(dtime)
table.insert(areaStrings, str)
end
local areaString = "Areas:"
local areaString = S("Areas:")
if #areaStrings > 0 then
areaString = areaString.."\n"..
table.concat(areaStrings, "\n")

View File

@ -1,3 +1,4 @@
local S = minetest.get_translator("areas")
local old_is_protected = minetest.is_protected
function minetest.is_protected(pos, name)
@ -11,7 +12,7 @@ minetest.register_on_protection_violation(function(pos, name)
if not areas:canInteract(pos, name) then
local owners = areas:getNodeOwners(pos)
minetest.chat_send_player(name,
("%s is protected by %s."):format(
S("@1 is protected by @2.",
minetest.pos_to_string(pos),
table.concat(owners, ", ")))
end

View File

@ -1,3 +1,4 @@
local S = minetest.get_translator("areas")
function areas:player_exists(name)
return minetest.get_auth_handler().get_auth(name) ~= nil
@ -212,8 +213,8 @@ function areas:canPlayerAddArea(pos1, pos2, name)
-- and if the area is too big.
if not self.config.self_protection or
not privs[areas.config.self_protection_privilege] then
return false, "Self protection is disabled or you do not have"
.." the necessary privilege."
return false, S("Self protection is disabled or you do not have"
.." the necessary privilege.")
end
local max_size = privs.areas_high_limit and
@ -223,7 +224,7 @@ function areas:canPlayerAddArea(pos1, pos2, name)
(pos2.x - pos1.x) > max_size.x or
(pos2.y - pos1.y) > max_size.y or
(pos2.z - pos1.z) > max_size.z then
return false, "Area is too big."
return false, S("Area is too big.")
end
-- Check number of areas the user has and make sure it not above the max
@ -237,16 +238,16 @@ function areas:canPlayerAddArea(pos1, pos2, name)
self.config.self_protection_max_areas_high or
self.config.self_protection_max_areas
if count >= max_areas then
return false, "You have reached the maximum amount of"
.." areas that you are allowed to protect."
return false, S("You have reached the maximum amount of"
.." areas that you are allowed to protect.")
end
-- Check intersecting areas
local can, id = self:canInteractInArea(pos1, pos2, name)
if not can then
local area = self.areas[id]
return false, ("The area intersects with %s [%u] (%s).")
:format(area.name, id, area.owner)
return false, S("The area intersects with @1 [@2] (@3).",
area.name, id, area.owner)
end
return true

View File

@ -1,25 +1,26 @@
-- This file contains functions to convert from
-- the old areas format and other compatability code.
local S = minetest.get_translator("areas")
minetest.register_chatcommand("legacy_load_areas", {
params = "<version>",
description = "Loads, converts, and saves the areas from"
.." a legacy save file.",
params = S("<version>"),
description = S("Loads, converts, and saves the areas from"
.." a legacy save file."),
privs = {areas=true, server=true},
func = function(name, param)
minetest.chat_send_player(name, "Converting areas...")
minetest.chat_send_player(name, S("Converting areas…"))
local version = tonumber(param)
if version == 0 then
local err = areas:node_ownership_load()
if err then
minetest.chat_send_player(name, "Error loading legacy file: "..err)
minetest.chat_send_player(name, S("Error loading legacy file: @1", err))
return
end
else
minetest.chat_send_player(name, "Invalid version number. (0 allowed)")
minetest.chat_send_player(name, S("Invalid version number. (0 allowed)"))
return
end
minetest.chat_send_player(name, "Legacy file loaded.")
minetest.chat_send_player(name, S("Legacy file loaded."))
for k, area in pairs(areas.areas) do
-- New position format
@ -34,15 +35,15 @@ minetest.register_chatcommand("legacy_load_areas", {
areas:sortPos(area.pos1, area.pos2)
-- Add name
area.name = "unnamed"
area.name = S("unnamed")
-- Remove ID
area.id = nil
end
minetest.chat_send_player(name, "Table format updated.")
minetest.chat_send_player(name, S("Table format updated."))
areas:save()
minetest.chat_send_player(name, "Converted areas saved. Done.")
minetest.chat_send_player(name, S("Converted areas saved. Done."))
end
})
@ -130,7 +131,7 @@ if areas.config.legacy_table then
{x=a.x2, y=a.y2, z=a.z2}
a.x1, a.y1, a.z1, a.x2, a.y2, a.z2 =
nil, nil, nil, nil, nil, nil
a.name = a.name or "unnamed"
a.name = a.name or S("unnamed")
a.id = nil
return rawset(areas.areas, key, a)
end

125
areas/locale/areas.fr.tr Normal file
View File

@ -0,0 +1,125 @@
# textdomain: areas
### chatcommands.lua ###
<AreaName>=<NomZone>
<NewOwner>=<NouveauPropriétaire>
<ParentID>=<IDZonePrincipale>
<PlayerName>=<NomJoueur>
<newName>=<NouveauNom>
@1 has given you control over the area "@2" (ID @3).=@1 vous a donné le contrôle de la zone "@2" (ID @3).
@1 spanning up to @2x@3x@4.=@1 sétendant jusquà @2x@3x@4.
A regular expression is required.=Une expression régulière est requise.
Area @1 does not exist or is not owned by you.=La zone @1 nexiste pas ou ne vous appartient pas.
Area closed for faction members.=Zone fermée aux membres de la faction.
Area closed.=Zone fermée.
Area does not exist.=La zone nexiste pas.
Area opened for faction members.=Zone ouverte aux membres de la faction.
Area opened.=Zone ouverte.
Area protected. ID: @1=Zone protégée. ID : @1
Area renamed.=Zone renommée.
Area successfully moved.=Zone déplacée avec succès.
Change the owner of an area using its ID=Change le propriétaire dune zone en utilisant son ID.
Find areas using a Lua regular expression=Trouve les zones en utilisant une expression régulière Lua.
Get information about area configuration and usage.=Obtient des informations sur la configuration des zones et lutilisation des zones.
Give a player access to a sub-area beetween two positions that have already been protected, Use set_owner if you don't want the parent to be set.=Donne au joueur accès aux sous-zones entre deux positions qui ont déjà été protégées ; utilisez set_owner si vous ne voulez pas que la zone pricipale soit définie.
Invalid regular expression.=Expression régulière invalide.
Limit: @1 areas=Limite: @1 zones.
Limit: no area count limit=Limite: pas de limite de nombre de zones.
List your areas, or all areas if you are an admin.=Liste vos zones, ou toutes les zones si vous êtes administrateur.
Move (or resize) an area to the current positions.=Déplace (ou redimensionne) une zone aux positions actuelles.
No matches found.=Aucun résultat.
No visible areas.=Pas de zone visible.
Owner changed.=Propriétaire changé.
Players with the "@1" privilege can protect up to @2 areas=Les joueurs avec le privilège "@1" peuvent protéger jusquà @2 zones
Protect an area between two positions and give a player access to it without setting the parent of the area to any existing area=Protège une zone entre deux positions et donne à un joueur accès à cette zone sans définir la zone principale de cette zone ni aucune zone existante.
Protect your own area=Protège votre zone.
Recursively remove areas using an ID=Supprime les zones récursivement en utilisant un ID.
Remove an area using an ID=Supprime une zone en utilisant son ID.
Removed area @1=Zone @1 supprimée.
Removed area @1 and it's sub areas.=Zone @1 et ses sous-zones supprimées.
Rename an area that you own=Renomme une zone qui vous appartient.
Self protection is disabled.=Lautoprotection est désactivée.
Self protection is enabled.=Lautoprotection est activée.
That area doesn't exist.=La zone nexiste pas.
The player "@1" does not exist.=Le joueur "@1" nexiste pas.
Toggle an area open (anyone can interact) or closed=Bascule entre zone ouverte (tout le monde peut intéragir) ou fermée.
Toggle an area open/closed for members in your faction.=Bascule entre zone ouverte/fermée pour les membres de votre faction.
You are an area administrator ("areas" privilege).=Vous êtes un administrateur de zone (privilège "areas").
You can protect areas=Vous pouvez protéger des zones.
You can't protect that area.=Vous ne pouvez pas protéger cette zone.
You can't protect that area: @1=Vous ne pouvez pas protéger cette zone : @1.
You don't have the necessary privilege ("@1").=Vous navez pas le privilège nécessaire ("@1").
You don't own that area.=Vous ne possédez pas cette zone.
You have @1 areas.=Vous avez @1 zones.
You have been granted control over area #@1. Type /list_areas to show your areas.=Vous avez reçu lautorisation de contrôler la zone #@1.
You have extended area protection limits ("areas_high_limit" privilege).=Votre limite de protection de zones est étendue (privilège "areas_high_limit").
You have the necessary privilege ("@1").=Vous avez le privilège nécessaire ("@1").
You need to select an area first.=Vous devez sélectionner une zone dabord.
### chatcommands.lua ###
### pos.lua ###
<ID>=<ID>
Invalid usage, see /help @1.=Utilisation incorrecte, voir /help @1.
### hud.lua ###
:open= : ouverte
Areas:=Zones :
### interact.lua ###
@1 is protected by @2.=@1 est protégée par @2.
### internal.lua ###
Area is too big.=La zone est trop grande.
Self protection is disabled or you do not have the necessary privilege.=Lautoprotection est désactivée ou vous navez pas le privilège nécessaire.
The area intersects with @1 [@2] (@3).=La zone a une intersection avec @1 [@2] (@3).
You have reached the maximum amount of areas that you are allowed to protect.=Vous avez atteint le nombre maximum de zones que vous êtes autorisé à protéger.
### legacy.lua ###
<version>=<version>
Converted areas saved. Done.=Zones converties sauvegardées. Fait.
Converting areas…=Conversion des zones…
Error loading legacy file: @1=Erreur lors du chargement du fichier : @1
Invalid version number. (0 allowed)=Numéro de version invalide. (0 autorisé)
Legacy file loaded.=Fichier obsolète chargé.
Loads, converts, and saves the areas from a legacy save file.=Charge, fait la conversion et sauvegarde les zones depuis un fichier de sauvegarde obsolète.
Table format updated.=Format de tableau mis à jour.
unnamed=Non nommé
### pos.lua ###
<not set>=<no définie>
Area @1 selected.=Zone @1 sélectionnée.
Area position @1 set to @2=Position @1 de la zone définie à @2.
Position @1 set to @2=Position @1 définie à @2.
Position @1: =Position @1 :
Select an area by ID.=Sélectionnez une zone par son ID.
Select position @1 by punching a node.=Sélectionnez une position en frappant un bloc.
Select positions by punching two nodes.=Sélectionnez une position en frappant deux blocs.
Set area protection region position @1 to your location or the one specified=Définit la position @1 de la région de protection de zone à votre position ou à celle spécifiée.
Set area protection region, position 1, or position 2 by punching nodes, or display the region=Définit la région de protection de zone, la position 1, ou la position 2 en frappant des blocs, ou en affichant la région.
The area @1 does not exist.=La zone @1 nexiste pas.
Unable to get position.=Impossible dobtenir la position.
Unknown subcommand: @1=Sous-commande inconnue : @1

125
areas/locale/template.txt Normal file
View File

@ -0,0 +1,125 @@
# textdomain: areas
### chatcommands.lua ###
<AreaName>=
<NewOwner>=
<ParentID>=
<PlayerName>=
<newName>=
@1 has given you control over the area "@2" (ID @3).=
@1 spanning up to @2x@3x@4.=
A regular expression is required.=
Area @1 does not exist or is not owned by you.=
Area closed for faction members.=
Area closed.=
Area does not exist.=
Area opened for faction members.=
Area opened.=
Area protected. ID: @1=
Area renamed.=
Area successfully moved.=
Change the owner of an area using its ID=
Find areas using a Lua regular expression=
Get information about area configuration and usage.=
Give a player access to a sub-area beetween two positions that have already been protected, Use set_owner if you don't want the parent to be set.=
Invalid regular expression.=
Limit: @1 areas=
Limit: no area count limit=
List your areas, or all areas if you are an admin.=
Move (or resize) an area to the current positions.=
No matches found.=
No visible areas.=
Owner changed.=
Players with the "@1" privilege can protect up to @2 areas=
Protect an area between two positions and give a player access to it without setting the parent of the area to any existing area=
Protect your own area=
Recursively remove areas using an ID=
Remove an area using an ID=
Removed area @1=
Removed area @1 and it's sub areas.=
Rename an area that you own=
Self protection is disabled.=
Self protection is enabled.=
That area doesn't exist.=
The player "@1" does not exist.=
Toggle an area open (anyone can interact) or closed=
Toggle an area open/closed for members in your faction.=
You are an area administrator ("areas" privilege).=
You can protect areas=
You can't protect that area.=
You can't protect that area: @1=
You don't have the necessary privilege ("@1").=
You don't own that area.=
You have @1 areas.=
You have been granted control over area #@1. Type /list_areas to show your areas.=
You have extended area protection limits ("areas_high_limit" privilege).=
You have the necessary privilege ("@1").=
You need to select an area first.=
### chatcommands.lua ###
### pos.lua ###
<ID>=
Invalid usage, see /help @1.=
### hud.lua ###
:open=
Areas:=
### interact.lua ###
@1 is protected by @2.=
### internal.lua ###
Area is too big.=
Self protection is disabled or you do not have the necessary privilege.=
The area intersects with @1 [@2] (@3).=
You have reached the maximum amount of areas that you are allowed to protect.=
### legacy.lua ###
<version>=
Converted areas saved. Done.=
Converting areas…=
Error loading legacy file: @1=
Invalid version number. (0 allowed)=
Legacy file loaded.=
Loads, converts, and saves the areas from a legacy save file.=
Table format updated.=
unnamed=
### pos.lua ###
<not set>=
Area @1 selected.=
Area position @1 set to @2=
Position @1 set to @2=
Position @1: =
Select an area by ID.=
Select position @1 by punching a node.=
Select positions by punching two nodes.=
Set area protection region position @1 to your location or the one specified=
Set area protection region, position 1, or position 2 by punching nodes, or display the region=
The area @1 does not exist.=
Unable to get position.=
Unknown subcommand: @1=

View File

@ -1,4 +1,4 @@
local S = minetest.get_translator("areas")
-- I could depend on WorldEdit for this, but you need to have the 'worldedit'
-- permission to use those commands and you don't have
-- /area_pos{1,2} [X Y Z|X,Y,Z].
@ -22,27 +22,27 @@ local function posLimit(pos)
end
minetest.register_chatcommand("select_area", {
params = "<ID>",
description = "Select a area by id.",
params = S("<ID>"),
description = S("Select an area by ID."),
func = function(name, param)
local id = tonumber(param)
if not id then
return false, "Invalid usage, see /help select_area."
return false, S("Invalid usage, see /help @1.", "select_area")
end
if not areas.areas[id] then
return false, "The area "..id.." does not exist."
return false, S("The area @1 does not exist.", id)
end
areas:setPos1(name, areas.areas[id].pos1)
areas:setPos2(name, areas.areas[id].pos2)
return true, "Area "..id.." selected."
return true, S("Area @1 selected.", id)
end,
})
minetest.register_chatcommand("area_pos1", {
params = "[X Y Z|X,Y,Z]",
description = "Set area protection region position 1 to your"
.." location or the one specified",
description = S("Set area protection region position @1 to your"
.." location or the one specified", "1"),
privs = {},
func = function(name, param)
local pos
@ -55,22 +55,22 @@ minetest.register_chatcommand("area_pos1", {
if player then
pos = player:get_pos()
else
return false, "Unable to get position."
return false, S("Unable to get position.")
end
else
return false, "Invalid usage, see /help area_pos1."
return false, S("Invalid usage, see /help @1.", "area_pos1")
end
pos = posLimit(vector.round(pos))
areas:setPos1(name, pos)
return true, "Area position 1 set to "
..minetest.pos_to_string(pos)
return true, S("Area position @1 set to @2", "1",
minetest.pos_to_string(pos))
end,
})
minetest.register_chatcommand("area_pos2", {
params = "[X Y Z|X,Y,Z]",
description = "Set area protection region position 2 to your"
.." location or the one specified",
description = S("Set area protection region position @1 to your"
.." location or the one specified", "2"),
func = function(name, param)
local pos
local found, _, x, y, z = param:find(
@ -82,48 +82,48 @@ minetest.register_chatcommand("area_pos2", {
if player then
pos = player:get_pos()
else
return false, "Unable to get position."
return false, S("Unable to get position.")
end
else
return false, "Invalid usage, see /help area_pos2."
return false, S("Invalid usage, see /help @1.", "area_pos2")
end
pos = posLimit(vector.round(pos))
areas:setPos2(name, pos)
return true, "Area position 2 set to "
..minetest.pos_to_string(pos)
return true, S("Area position @1 set to @2", "2",
minetest.pos_to_string(pos))
end,
})
minetest.register_chatcommand("area_pos", {
params = "set/set1/set2/get",
description = "Set area protection region, position 1, or position 2"
.." by punching nodes, or display the region",
description = S("Set area protection region, position 1, or position 2"
.." by punching nodes, or display the region"),
func = function(name, param)
if param == "set" then -- Set both area positions
areas.set_pos[name] = "pos1"
return true, "Select positions by punching two nodes."
return true, S("Select positions by punching two nodes.")
elseif param == "set1" then -- Set area position 1
areas.set_pos[name] = "pos1only"
return true, "Select position 1 by punching a node."
return true, S("Select position @1 by punching a node.", "1")
elseif param == "set2" then -- Set area position 2
areas.set_pos[name] = "pos2"
return true, "Select position 2 by punching a node."
return true, S("Select position @1 by punching a node.", "2")
elseif param == "get" then -- Display current area positions
local pos1str, pos2str = "Position 1: ", "Position 2: "
local pos1str, pos2str = S("Position @1: ", "1"), S("Position @1: ", "2")
if areas.pos1[name] then
pos1str = pos1str..minetest.pos_to_string(areas.pos1[name])
else
pos1str = pos1str.."<not set>"
pos1str = pos1str..S("<not set>")
end
if areas.pos2[name] then
pos2str = pos2str..minetest.pos_to_string(areas.pos2[name])
else
pos2str = pos2str.."<not set>"
pos2str = pos2str..S("<not set>")
end
return true, pos1str.."\n"..pos2str
else
return false, "Unknown subcommand: "..param
return false, S("Unknown subcommand: @1", param)
end
end,
})
@ -159,22 +159,22 @@ minetest.register_on_punchnode(function(pos, node, puncher)
areas.markPos1(name)
areas.set_pos[name] = "pos2"
minetest.chat_send_player(name,
"Position 1 set to "
..minetest.pos_to_string(pos))
S("Position @1 set to @2", "1",
minetest.pos_to_string(pos)))
elseif areas.set_pos[name] == "pos1only" then
areas.pos1[name] = pos
areas.markPos1(name)
areas.set_pos[name] = nil
minetest.chat_send_player(name,
"Position 1 set to "
..minetest.pos_to_string(pos))
S("Position @1 set to @2", "1",
minetest.pos_to_string(pos)))
elseif areas.set_pos[name] == "pos2" then
areas.pos2[name] = pos
areas.markPos2(name)
areas.set_pos[name] = nil
minetest.chat_send_player(name,
"Position 2 set to "
..minetest.pos_to_string(pos))
S("Position @1 set to @2", "2",
minetest.pos_to_string(pos)))
end
end
end)

View File

@ -108,11 +108,14 @@ minetest.register_craft( {
recipe = {"dye:black", "dye:black", "dye:white"}
})
-- only add light grey recipe if unifieddye mod isnt present (conflict)
if not minetest.get_modpath("unifieddyes") then
minetest.register_craft( {
type = "shapeless",
output = "dye:grey 3",
recipe = {"dye:black", "dye:white", "dye:white"}
})
end
minetest.register_craft( {
type = "shapeless",

View File

@ -0,0 +1,33 @@
# textdomain: basic_materials
Silicon lump=Silikonklumpen
Simple Integrated Circuit=einfacher Integrierter Schaltkreis
Simple Motor=einfacher Motor
Heating element=Heizelement
Simple energy crystal=einfacher Energiekristall
Spool of steel wire=Spule mit Stahldraht
Spool of copper wire=Spule mit Kupferdraht
Spool of silver wire=Spule mit Silberdraht
Spool of gold wire=Spule mit Golddraht
Steel Strip=Stahlstreifen
Copper Strip=Kupferstreifen
Steel Bar=Stahlstab
Chainlinks (brass)=Messing-Kettenglieder
Chainlinks (steel)=Stahl-Kettenglieder
Brass Ingot=Messingbarren
Steel gear=Stahlzahnrad
Padlock=Vorhängeschloss
Chain (steel, hanging)=Stahlkette
Chain (brass, hanging)=Messingkette
Brass Block=Messingblock
Oil extract=raffiniertes Öl
Unprocessed paraffin=unbearbeitetes Paraffin
Uncooked Terracotta Base=ungebranntes Terrakotta
Wet Cement=nasser Zement
Cement=Zement
Concrete Block=Betonblock
Plastic sheet=Kunststoffplatte
Plastic strips=Kunststoffstreifen
Empty wire spool=leere Drahtspule

View File

@ -245,6 +245,18 @@ minetest.register_craft( {
},
})
if not minetest.get_modpath("moreores") then
-- Without moreores, there still should be a way to create brass.
minetest.register_craft( {
output = "basic_materials:brass_ingot 9",
recipe = {
{"default:copper_ingot", "default:tin_ingot", "default:copper_ingot"},
{"default:gold_ingot", "default:copper_ingot", "default:gold_ingot"},
{"default:copper_ingot", "default:tin_ingot", "default:copper_ingot"},
},
})
end
minetest.register_craft( {
type = "shapeless",
output = "basic_materials:brass_ingot 9",

View File

@ -1,6 +1,5 @@
default
farming
intllib?
wool
bucket
ropes?

View File

@ -1,6 +1,4 @@
-- internationalization boilerplate
local MP = minetest.get_modpath(minetest.get_current_modname())
local S, NS = dofile(MP.."/intllib.lua")
local S = minetest.get_translator(minetest.get_current_modname())
minetest.register_alias("castle:hides", "castle_farming:hides")

418
castle_farming/i18n.py Normal file
View File

@ -0,0 +1,418 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Script to generate the template file and update the translation files.
# Copy the script into the mod or modpack root folder and run it there.
#
# Copyright (C) 2019 Joachim Stolberg, 2020 FaceDeer, 2020 Louis Royer
# LGPLv2.1+
from __future__ import print_function
import os, fnmatch, re, shutil, errno
from sys import argv as _argv
# Running params
params = {"recursive": False,
"help": False,
"mods": False,
"verbose": False,
"folders": []
}
# Available CLI options
options = {"recursive": ['--recursive', '-r'],
"help": ['--help', '-h'],
"mods": ['--installed-mods'],
"verbose": ['--verbose', '-v']
}
# Strings longer than this will have extra space added between
# them in the translation files to make it easier to distinguish their
# beginnings and endings at a glance
doublespace_threshold = 60
def set_params_folders(tab: list):
'''Initialize params["folders"] from CLI arguments.'''
# Discarding argument 0 (tool name)
for param in tab[1:]:
stop_param = False
for option in options:
if param in options[option]:
stop_param = True
break
if not stop_param:
params["folders"].append(os.path.abspath(param))
def set_params(tab: list):
'''Initialize params from CLI arguments.'''
for option in options:
for option_name in options[option]:
if option_name in tab:
params[option] = True
break
def print_help(name):
'''Prints some help message.'''
print(f'''SYNOPSIS
{name} [OPTIONS] [PATHS...]
DESCRIPTION
{', '.join(options["help"])}
prints this help message
{', '.join(options["recursive"])}
run on all subfolders of paths given
{', '.join(options["mods"])}
run on locally installed modules
{', '.join(options["verbose"])}
add output information
''')
def main():
'''Main function'''
set_params(_argv)
set_params_folders(_argv)
if params["help"]:
print_help(_argv[0])
elif params["recursive"] and params["mods"]:
print("Option --installed-mods is incompatible with --recursive")
else:
# Add recursivity message
print("Running ", end='')
if params["recursive"]:
print("recursively ", end='')
# Running
if params["mods"]:
print(f"on all locally installed modules in {os.path.abspath('~/.minetest/mods/')}")
run_all_subfolders("~/.minetest/mods")
elif len(params["folders"]) >= 2:
print("on folder list:", params["folders"])
for f in params["folders"]:
if params["recursive"]:
run_all_subfolders(f)
else:
update_folder(f)
elif len(params["folders"]) == 1:
print("on folder", params["folders"][0])
if params["recursive"]:
run_all_subfolders(params["folders"][0])
else:
update_folder(params["folders"][0])
else:
print("on folder", os.path.abspath("./"))
if params["recursive"]:
run_all_subfolders(os.path.abspath("./"))
else:
update_folder(os.path.abspath("./"))
#group 2 will be the string, groups 1 and 3 will be the delimiters (" or ')
#See https://stackoverflow.com/questions/46967465/regex-match-text-in-either-single-or-double-quote
pattern_lua = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL)
pattern_lua_bracketed = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*\[\[(.*?)\]\][\s,\)]', re.DOTALL)
# Handles "concatenation" .. " of strings"
pattern_concat = re.compile(r'["\'][\s]*\.\.[\s]*["\']', re.DOTALL)
pattern_tr = re.compile(r'(.+?[^@])=(.*)')
pattern_name = re.compile(r'^name[ ]*=[ ]*([^ \n]*)')
pattern_tr_filename = re.compile(r'\.tr$')
pattern_po_language_code = re.compile(r'(.*)\.po$')
#attempt to read the mod's name from the mod.conf file. Returns None on failure
def get_modname(folder):
try:
with open(os.path.join(folder, "mod.conf"), "r", encoding='utf-8') as mod_conf:
for line in mod_conf:
match = pattern_name.match(line)
if match:
return match.group(1)
except FileNotFoundError:
pass
return None
#If there are already .tr files in /locale, returns a list of their names
def get_existing_tr_files(folder):
out = []
for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
for name in files:
if pattern_tr_filename.search(name):
out.append(name)
return out
# A series of search and replaces that massage a .po file's contents into
# a .tr file's equivalent
def process_po_file(text):
# The first three items are for unused matches
text = re.sub(r'#~ msgid "', "", text)
text = re.sub(r'"\n#~ msgstr ""\n"', "=", text)
text = re.sub(r'"\n#~ msgstr "', "=", text)
# comment lines
text = re.sub(r'#.*\n', "", text)
# converting msg pairs into "=" pairs
text = re.sub(r'msgid "', "", text)
text = re.sub(r'"\nmsgstr ""\n"', "=", text)
text = re.sub(r'"\nmsgstr "', "=", text)
# various line breaks and escape codes
text = re.sub(r'"\n"', "", text)
text = re.sub(r'"\n', "\n", text)
text = re.sub(r'\\"', '"', text)
text = re.sub(r'\\n', '@n', text)
# remove header text
text = re.sub(r'=Project-Id-Version:.*\n', "", text)
# remove double-spaced lines
text = re.sub(r'\n\n', '\n', text)
return text
# Go through existing .po files and, if a .tr file for that language
# *doesn't* exist, convert it and create it.
# The .tr file that results will subsequently be reprocessed so
# any "no longer used" strings will be preserved.
# Note that "fuzzy" tags will be lost in this process.
def process_po_files(folder, modname):
for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
for name in files:
code_match = pattern_po_language_code.match(name)
if code_match == None:
continue
language_code = code_match.group(1)
tr_name = modname + "." + language_code + ".tr"
tr_file = os.path.join(root, tr_name)
if os.path.exists(tr_file):
if params["verbose"]:
print(f"{tr_name} already exists, ignoring {name}")
continue
fname = os.path.join(root, name)
with open(fname, "r", encoding='utf-8') as po_file:
if params["verbose"]:
print(f"Importing translations from {name}")
text = process_po_file(po_file.read())
with open(tr_file, "wt", encoding='utf-8') as tr_out:
tr_out.write(text)
# from https://stackoverflow.com/questions/600268/mkdir-p-functionality-in-python/600612#600612
# Creates a directory if it doesn't exist, silently does
# nothing if it already exists
def mkdir_p(path):
try:
os.makedirs(path)
except OSError as exc: # Python >2.5
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else: raise
# Converts the template dictionary to a text to be written as a file
# dKeyStrings is a dictionary of localized string to source file sets
# dOld is a dictionary of existing translations and comments from
# the previous version of this text
def strings_to_text(dkeyStrings, dOld, mod_name):
lOut = [f"# textdomain: {mod_name}\n"]
dGroupedBySource = {}
for key in dkeyStrings:
sourceList = list(dkeyStrings[key])
sourceList.sort()
sourceString = "\n".join(sourceList)
listForSource = dGroupedBySource.get(sourceString, [])
listForSource.append(key)
dGroupedBySource[sourceString] = listForSource
lSourceKeys = list(dGroupedBySource.keys())
lSourceKeys.sort()
for source in lSourceKeys:
localizedStrings = dGroupedBySource[source]
localizedStrings.sort()
lOut.append("")
lOut.append(source)
lOut.append("")
for localizedString in localizedStrings:
val = dOld.get(localizedString, {})
translation = val.get("translation", "")
comment = val.get("comment")
if len(localizedString) > doublespace_threshold and not lOut[-1] == "":
lOut.append("")
if comment != None:
lOut.append(comment)
lOut.append(f"{localizedString}={translation}")
if len(localizedString) > doublespace_threshold:
lOut.append("")
unusedExist = False
for key in dOld:
if key not in dkeyStrings:
val = dOld[key]
translation = val.get("translation")
comment = val.get("comment")
# only keep an unused translation if there was translated
# text or a comment associated with it
if translation != None and (translation != "" or comment):
if not unusedExist:
unusedExist = True
lOut.append("\n\n##### not used anymore #####\n")
if len(key) > doublespace_threshold and not lOut[-1] == "":
lOut.append("")
if comment != None:
lOut.append(comment)
lOut.append(f"{key}={translation}")
if len(key) > doublespace_threshold:
lOut.append("")
return "\n".join(lOut) + '\n'
# Writes a template.txt file
# dkeyStrings is the dictionary returned by generate_template
def write_template(templ_file, dkeyStrings, mod_name):
# read existing template file to preserve comments
existing_template = import_tr_file(templ_file)
text = strings_to_text(dkeyStrings, existing_template[0], mod_name)
mkdir_p(os.path.dirname(templ_file))
with open(templ_file, "wt", encoding='utf-8') as template_file:
template_file.write(text)
# Gets all translatable strings from a lua file
def read_lua_file_strings(lua_file):
lOut = []
with open(lua_file, encoding='utf-8') as text_file:
text = text_file.read()
#TODO remove comments here
text = re.sub(pattern_concat, "", text)
strings = []
for s in pattern_lua.findall(text):
strings.append(s[1])
for s in pattern_lua_bracketed.findall(text):
strings.append(s)
for s in strings:
s = re.sub(r'"\.\.\s+"', "", s)
s = re.sub("@[^@=0-9]", "@@", s)
s = s.replace('\\"', '"')
s = s.replace("\\'", "'")
s = s.replace("\n", "@n")
s = s.replace("\\n", "@n")
s = s.replace("=", "@=")
lOut.append(s)
return lOut
# Gets strings from an existing translation file
# returns both a dictionary of translations
# and the full original source text so that the new text
# can be compared to it for changes.
def import_tr_file(tr_file):
dOut = {}
text = None
if os.path.exists(tr_file):
with open(tr_file, "r", encoding='utf-8') as existing_file :
# save the full text to allow for comparison
# of the old version with the new output
text = existing_file.read()
existing_file.seek(0)
# a running record of the current comment block
# we're inside, to allow preceeding multi-line comments
# to be retained for a translation line
latest_comment_block = None
for line in existing_file.readlines():
line = line.rstrip('\n')
if line[:3] == "###":
# Reset comment block if we hit a header
latest_comment_block = None
continue
if line[:1] == "#":
# Save the comment we're inside
if not latest_comment_block:
latest_comment_block = line
else:
latest_comment_block = latest_comment_block + "\n" + line
continue
match = pattern_tr.match(line)
if match:
# this line is a translated line
outval = {}
outval["translation"] = match.group(2)
if latest_comment_block:
# if there was a comment, record that.
outval["comment"] = latest_comment_block
latest_comment_block = None
dOut[match.group(1)] = outval
return (dOut, text)
# Walks all lua files in the mod folder, collects translatable strings,
# and writes it to a template.txt file
# Returns a dictionary of localized strings to source file sets
# that can be used with the strings_to_text function.
def generate_template(folder, mod_name):
dOut = {}
for root, dirs, files in os.walk(folder):
for name in files:
if fnmatch.fnmatch(name, "*.lua"):
fname = os.path.join(root, name)
found = read_lua_file_strings(fname)
if params["verbose"]:
print(f"{fname}: {str(len(found))} translatable strings")
for s in found:
sources = dOut.get(s, set())
sources.add(f"### {os.path.basename(fname)} ###")
dOut[s] = sources
if len(dOut) == 0:
return None
templ_file = os.path.join(folder, "locale/template.txt")
write_template(templ_file, dOut, mod_name)
return dOut
# Updates an existing .tr file, copying the old one to a ".old" file
# if any changes have happened
# dNew is the data used to generate the template, it has all the
# currently-existing localized strings
def update_tr_file(dNew, mod_name, tr_file):
if params["verbose"]:
print(f"updating {tr_file}")
tr_import = import_tr_file(tr_file)
dOld = tr_import[0]
textOld = tr_import[1]
textNew = strings_to_text(dNew, dOld, mod_name)
if textOld and textOld != textNew:
print(f"{tr_file} has changed.")
shutil.copyfile(tr_file, f"{tr_file}.old")
with open(tr_file, "w", encoding='utf-8') as new_tr_file:
new_tr_file.write(textNew)
# Updates translation files for the mod in the given folder
def update_mod(folder):
modname = get_modname(folder)
if modname is not None:
process_po_files(folder, modname)
print(f"Updating translations for {modname}")
data = generate_template(folder, modname)
if data == None:
print(f"No translatable strings found in {modname}")
else:
for tr_file in get_existing_tr_files(folder):
update_tr_file(data, modname, os.path.join(folder, "locale/", tr_file))
else:
print("Unable to find modname in folder " + folder)
# Determines if the folder being pointed to is a mod or a mod pack
# and then runs update_mod accordingly
def update_folder(folder):
is_modpack = os.path.exists(os.path.join(folder, "modpack.txt")) or os.path.exists(os.path.join(folder, "modpack.conf"))
if is_modpack:
subfolders = [f.path for f in os.scandir(folder) if f.is_dir()]
for subfolder in subfolders:
update_mod(subfolder + "/")
else:
update_mod(folder)
print("Done.")
def run_all_subfolders(folder):
for modfolder in [f.path for f in os.scandir(folder) if f.is_dir()]:
update_folder(modfolder + "/")
main()

View File

@ -1,45 +0,0 @@
-- Fallback functions for when `intllib` is not installed.
-- Code released under Unlicense <http://unlicense.org>.
-- Get the latest version of this file at:
-- https://raw.githubusercontent.com/minetest-mods/intllib/master/lib/intllib.lua
local function format(str, ...)
local args = { ... }
local function repl(escape, open, num, close)
if escape == "" then
local replacement = tostring(args[tonumber(num)])
if open == "" then
replacement = replacement..close
end
return replacement
else
return "@"..open..num..close
end
end
return (str:gsub("(@?)@(%(?)(%d+)(%)?)", repl))
end
local gettext, ngettext
if minetest.get_modpath("intllib") then
if intllib.make_gettext_pair then
-- New method using gettext.
gettext, ngettext = intllib.make_gettext_pair()
else
-- Old method using text files.
gettext = intllib.Getter()
end
end
-- Fill in missing functions.
gettext = gettext or function(msgid, ...)
return format(msgid, ...)
end
ngettext = ngettext or function(msgid, msgid_plural, n, ...)
return format(n==1 and msgid or msgid_plural, ...)
end
return gettext, ngettext

View File

@ -0,0 +1,11 @@
# textdomain: castle_farming
### hides.lua ###
Hides=Escondite
### straw.lua ###
Bound Straw=Paja amarrada
Training Dummy=Maniqui de Entrenamiento

View File

@ -0,0 +1,11 @@
# textdomain: castle_farming
### hides.lua ###
Hides=Esconde-se
### straw.lua ###
Bound Straw=Palha Amarrada
Training Dummy=Vaca Parada de Madeira (para Laço)

View File

@ -1,30 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-02-27 00:59-0700\n"
"PO-Revision-Date: 2017-04-21 19:47-0500\n"
"Last-Translator: Carlos Barraza <carlosbarrazaes@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: Español\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: hides.lua:9
msgid "Hides"
msgstr "Escondite"
#: straw.lua:13
msgid "Bound Straw"
msgstr "Paja amarrada"
#: straw.lua:22
msgid "Training Dummy"
msgstr "Maniqui de Entrenamiento"

View File

@ -1,30 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) 2017
# This file is distributed under the same license as the castle_farming package.
# Caio Roberto <caiorrs@gmail.com>, 2017.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-02-27 00:59-0700\n"
"PO-Revision-Date: 2017-06-29 13:00-0330\n"
"Last-Translator: Caio Roberto <caiorrs@gmail.com>\n"
"Language-Team: Paulo Slomp FACED UFRGS <00009228@ufrgs.br>\n"
"Language: Brazilian Portuguese\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: hides.lua:9
msgid "Hides"
msgstr "Esconde-se"
#: straw.lua:13
msgid "Bound Straw"
msgstr "Palha Amarrada"
#: straw.lua:22
msgid "Training Dummy"
msgstr "Vaca Parada de Madeira (para Laço)"

View File

@ -1,30 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-02-27 00:59-0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: hides.lua:9
msgid "Hides"
msgstr ""
#: straw.lua:13
msgid "Bound Straw"
msgstr ""
#: straw.lua:22
msgid "Training Dummy"
msgstr ""

View File

@ -0,0 +1,11 @@
# textdomain: castle_farming
### hides.lua ###
Hides=
### straw.lua ###
Bound Straw=
Training Dummy=

View File

@ -1 +1,4 @@
name = castle_farming
depends = default, farming, wool, bucket
optional_depends = ropes
description = Contains farming products useful for decorating a castle

View File

@ -5,9 +5,7 @@ minetest.register_alias_force("cottages:straw_bale", "castle_farming:bound_straw
minetest.register_alias_force("darkage:straw_bale", "castle_farming:bound_straw")
minetest.register_alias_force("castle:bound_straw", "castle_farming:bound_straw")
-- internationalization boilerplate
local MP = minetest.get_modpath(minetest.get_current_modname())
local S, NS = dofile(MP.."/intllib.lua")
local S = minetest.get_translator(minetest.get_current_modname())
minetest.register_node("castle_farming:bound_straw", {
description = S("Bound Straw"),

Binary file not shown.

Before

Width:  |  Height:  |  Size: 618 B

After

Width:  |  Height:  |  Size: 614 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 756 B

After

Width:  |  Height:  |  Size: 732 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 803 B

After

Width:  |  Height:  |  Size: 798 B

View File

@ -1,8 +1,6 @@
if not minetest.get_modpath("fire") then return end
-- internationalization boilerplate
local MP = minetest.get_modpath(minetest.get_current_modname())
local S, NS = dofile(MP.."/intllib.lua")
local S = minetest.get_translator(minetest.get_current_modname())
local brasier_longdesc = S("A brasier for producing copious amounts of light and heat.")
local brasier_usagehelp = S("To ignite the brasier place a flammable fuel in its inventory slot. A lump of coal will burn for about half an hour.")

View File

@ -1,5 +1,4 @@
default
intllib?
fire?
castle_masonry?
hopper?

418
castle_lighting/i18n.py Normal file
View File

@ -0,0 +1,418 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Script to generate the template file and update the translation files.
# Copy the script into the mod or modpack root folder and run it there.
#
# Copyright (C) 2019 Joachim Stolberg, 2020 FaceDeer, 2020 Louis Royer
# LGPLv2.1+
from __future__ import print_function
import os, fnmatch, re, shutil, errno
from sys import argv as _argv
# Running params
params = {"recursive": False,
"help": False,
"mods": False,
"verbose": False,
"folders": []
}
# Available CLI options
options = {"recursive": ['--recursive', '-r'],
"help": ['--help', '-h'],
"mods": ['--installed-mods'],
"verbose": ['--verbose', '-v']
}
# Strings longer than this will have extra space added between
# them in the translation files to make it easier to distinguish their
# beginnings and endings at a glance
doublespace_threshold = 60
def set_params_folders(tab: list):
'''Initialize params["folders"] from CLI arguments.'''
# Discarding argument 0 (tool name)
for param in tab[1:]:
stop_param = False
for option in options:
if param in options[option]:
stop_param = True
break
if not stop_param:
params["folders"].append(os.path.abspath(param))
def set_params(tab: list):
'''Initialize params from CLI arguments.'''
for option in options:
for option_name in options[option]:
if option_name in tab:
params[option] = True
break
def print_help(name):
'''Prints some help message.'''
print(f'''SYNOPSIS
{name} [OPTIONS] [PATHS...]
DESCRIPTION
{', '.join(options["help"])}
prints this help message
{', '.join(options["recursive"])}
run on all subfolders of paths given
{', '.join(options["mods"])}
run on locally installed modules
{', '.join(options["verbose"])}
add output information
''')
def main():
'''Main function'''
set_params(_argv)
set_params_folders(_argv)
if params["help"]:
print_help(_argv[0])
elif params["recursive"] and params["mods"]:
print("Option --installed-mods is incompatible with --recursive")
else:
# Add recursivity message
print("Running ", end='')
if params["recursive"]:
print("recursively ", end='')
# Running
if params["mods"]:
print(f"on all locally installed modules in {os.path.abspath('~/.minetest/mods/')}")
run_all_subfolders("~/.minetest/mods")
elif len(params["folders"]) >= 2:
print("on folder list:", params["folders"])
for f in params["folders"]:
if params["recursive"]:
run_all_subfolders(f)
else:
update_folder(f)
elif len(params["folders"]) == 1:
print("on folder", params["folders"][0])
if params["recursive"]:
run_all_subfolders(params["folders"][0])
else:
update_folder(params["folders"][0])
else:
print("on folder", os.path.abspath("./"))
if params["recursive"]:
run_all_subfolders(os.path.abspath("./"))
else:
update_folder(os.path.abspath("./"))
#group 2 will be the string, groups 1 and 3 will be the delimiters (" or ')
#See https://stackoverflow.com/questions/46967465/regex-match-text-in-either-single-or-double-quote
pattern_lua = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL)
pattern_lua_bracketed = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*\[\[(.*?)\]\][\s,\)]', re.DOTALL)
# Handles "concatenation" .. " of strings"
pattern_concat = re.compile(r'["\'][\s]*\.\.[\s]*["\']', re.DOTALL)
pattern_tr = re.compile(r'(.+?[^@])=(.*)')
pattern_name = re.compile(r'^name[ ]*=[ ]*([^ \n]*)')
pattern_tr_filename = re.compile(r'\.tr$')
pattern_po_language_code = re.compile(r'(.*)\.po$')
#attempt to read the mod's name from the mod.conf file. Returns None on failure
def get_modname(folder):
try:
with open(os.path.join(folder, "mod.conf"), "r", encoding='utf-8') as mod_conf:
for line in mod_conf:
match = pattern_name.match(line)
if match:
return match.group(1)
except FileNotFoundError:
pass
return None
#If there are already .tr files in /locale, returns a list of their names
def get_existing_tr_files(folder):
out = []
for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
for name in files:
if pattern_tr_filename.search(name):
out.append(name)
return out
# A series of search and replaces that massage a .po file's contents into
# a .tr file's equivalent
def process_po_file(text):
# The first three items are for unused matches
text = re.sub(r'#~ msgid "', "", text)
text = re.sub(r'"\n#~ msgstr ""\n"', "=", text)
text = re.sub(r'"\n#~ msgstr "', "=", text)
# comment lines
text = re.sub(r'#.*\n', "", text)
# converting msg pairs into "=" pairs
text = re.sub(r'msgid "', "", text)
text = re.sub(r'"\nmsgstr ""\n"', "=", text)
text = re.sub(r'"\nmsgstr "', "=", text)
# various line breaks and escape codes
text = re.sub(r'"\n"', "", text)
text = re.sub(r'"\n', "\n", text)
text = re.sub(r'\\"', '"', text)
text = re.sub(r'\\n', '@n', text)
# remove header text
text = re.sub(r'=Project-Id-Version:.*\n', "", text)
# remove double-spaced lines
text = re.sub(r'\n\n', '\n', text)
return text
# Go through existing .po files and, if a .tr file for that language
# *doesn't* exist, convert it and create it.
# The .tr file that results will subsequently be reprocessed so
# any "no longer used" strings will be preserved.
# Note that "fuzzy" tags will be lost in this process.
def process_po_files(folder, modname):
for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
for name in files:
code_match = pattern_po_language_code.match(name)
if code_match == None:
continue
language_code = code_match.group(1)
tr_name = modname + "." + language_code + ".tr"
tr_file = os.path.join(root, tr_name)
if os.path.exists(tr_file):
if params["verbose"]:
print(f"{tr_name} already exists, ignoring {name}")
continue
fname = os.path.join(root, name)
with open(fname, "r", encoding='utf-8') as po_file:
if params["verbose"]:
print(f"Importing translations from {name}")
text = process_po_file(po_file.read())
with open(tr_file, "wt", encoding='utf-8') as tr_out:
tr_out.write(text)
# from https://stackoverflow.com/questions/600268/mkdir-p-functionality-in-python/600612#600612
# Creates a directory if it doesn't exist, silently does
# nothing if it already exists
def mkdir_p(path):
try:
os.makedirs(path)
except OSError as exc: # Python >2.5
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else: raise
# Converts the template dictionary to a text to be written as a file
# dKeyStrings is a dictionary of localized string to source file sets
# dOld is a dictionary of existing translations and comments from
# the previous version of this text
def strings_to_text(dkeyStrings, dOld, mod_name):
lOut = [f"# textdomain: {mod_name}\n"]
dGroupedBySource = {}
for key in dkeyStrings:
sourceList = list(dkeyStrings[key])
sourceList.sort()
sourceString = "\n".join(sourceList)
listForSource = dGroupedBySource.get(sourceString, [])
listForSource.append(key)
dGroupedBySource[sourceString] = listForSource
lSourceKeys = list(dGroupedBySource.keys())
lSourceKeys.sort()
for source in lSourceKeys:
localizedStrings = dGroupedBySource[source]
localizedStrings.sort()
lOut.append("")
lOut.append(source)
lOut.append("")
for localizedString in localizedStrings:
val = dOld.get(localizedString, {})
translation = val.get("translation", "")
comment = val.get("comment")
if len(localizedString) > doublespace_threshold and not lOut[-1] == "":
lOut.append("")
if comment != None:
lOut.append(comment)
lOut.append(f"{localizedString}={translation}")
if len(localizedString) > doublespace_threshold:
lOut.append("")
unusedExist = False
for key in dOld:
if key not in dkeyStrings:
val = dOld[key]
translation = val.get("translation")
comment = val.get("comment")
# only keep an unused translation if there was translated
# text or a comment associated with it
if translation != None and (translation != "" or comment):
if not unusedExist:
unusedExist = True
lOut.append("\n\n##### not used anymore #####\n")
if len(key) > doublespace_threshold and not lOut[-1] == "":
lOut.append("")
if comment != None:
lOut.append(comment)
lOut.append(f"{key}={translation}")
if len(key) > doublespace_threshold:
lOut.append("")
return "\n".join(lOut) + '\n'
# Writes a template.txt file
# dkeyStrings is the dictionary returned by generate_template
def write_template(templ_file, dkeyStrings, mod_name):
# read existing template file to preserve comments
existing_template = import_tr_file(templ_file)
text = strings_to_text(dkeyStrings, existing_template[0], mod_name)
mkdir_p(os.path.dirname(templ_file))
with open(templ_file, "wt", encoding='utf-8') as template_file:
template_file.write(text)
# Gets all translatable strings from a lua file
def read_lua_file_strings(lua_file):
lOut = []
with open(lua_file, encoding='utf-8') as text_file:
text = text_file.read()
#TODO remove comments here
text = re.sub(pattern_concat, "", text)
strings = []
for s in pattern_lua.findall(text):
strings.append(s[1])
for s in pattern_lua_bracketed.findall(text):
strings.append(s)
for s in strings:
s = re.sub(r'"\.\.\s+"', "", s)
s = re.sub("@[^@=0-9]", "@@", s)
s = s.replace('\\"', '"')
s = s.replace("\\'", "'")
s = s.replace("\n", "@n")
s = s.replace("\\n", "@n")
s = s.replace("=", "@=")
lOut.append(s)
return lOut
# Gets strings from an existing translation file
# returns both a dictionary of translations
# and the full original source text so that the new text
# can be compared to it for changes.
def import_tr_file(tr_file):
dOut = {}
text = None
if os.path.exists(tr_file):
with open(tr_file, "r", encoding='utf-8') as existing_file :
# save the full text to allow for comparison
# of the old version with the new output
text = existing_file.read()
existing_file.seek(0)
# a running record of the current comment block
# we're inside, to allow preceeding multi-line comments
# to be retained for a translation line
latest_comment_block = None
for line in existing_file.readlines():
line = line.rstrip('\n')
if line[:3] == "###":
# Reset comment block if we hit a header
latest_comment_block = None
continue
if line[:1] == "#":
# Save the comment we're inside
if not latest_comment_block:
latest_comment_block = line
else:
latest_comment_block = latest_comment_block + "\n" + line
continue
match = pattern_tr.match(line)
if match:
# this line is a translated line
outval = {}
outval["translation"] = match.group(2)
if latest_comment_block:
# if there was a comment, record that.
outval["comment"] = latest_comment_block
latest_comment_block = None
dOut[match.group(1)] = outval
return (dOut, text)
# Walks all lua files in the mod folder, collects translatable strings,
# and writes it to a template.txt file
# Returns a dictionary of localized strings to source file sets
# that can be used with the strings_to_text function.
def generate_template(folder, mod_name):
dOut = {}
for root, dirs, files in os.walk(folder):
for name in files:
if fnmatch.fnmatch(name, "*.lua"):
fname = os.path.join(root, name)
found = read_lua_file_strings(fname)
if params["verbose"]:
print(f"{fname}: {str(len(found))} translatable strings")
for s in found:
sources = dOut.get(s, set())
sources.add(f"### {os.path.basename(fname)} ###")
dOut[s] = sources
if len(dOut) == 0:
return None
templ_file = os.path.join(folder, "locale/template.txt")
write_template(templ_file, dOut, mod_name)
return dOut
# Updates an existing .tr file, copying the old one to a ".old" file
# if any changes have happened
# dNew is the data used to generate the template, it has all the
# currently-existing localized strings
def update_tr_file(dNew, mod_name, tr_file):
if params["verbose"]:
print(f"updating {tr_file}")
tr_import = import_tr_file(tr_file)
dOld = tr_import[0]
textOld = tr_import[1]
textNew = strings_to_text(dNew, dOld, mod_name)
if textOld and textOld != textNew:
print(f"{tr_file} has changed.")
shutil.copyfile(tr_file, f"{tr_file}.old")
with open(tr_file, "w", encoding='utf-8') as new_tr_file:
new_tr_file.write(textNew)
# Updates translation files for the mod in the given folder
def update_mod(folder):
modname = get_modname(folder)
if modname is not None:
process_po_files(folder, modname)
print(f"Updating translations for {modname}")
data = generate_template(folder, modname)
if data == None:
print(f"No translatable strings found in {modname}")
else:
for tr_file in get_existing_tr_files(folder):
update_tr_file(data, modname, os.path.join(folder, "locale/", tr_file))
else:
print("Unable to find modname in folder " + folder)
# Determines if the folder being pointed to is a mod or a mod pack
# and then runs update_mod accordingly
def update_folder(folder):
is_modpack = os.path.exists(os.path.join(folder, "modpack.txt")) or os.path.exists(os.path.join(folder, "modpack.conf"))
if is_modpack:
subfolders = [f.path for f in os.scandir(folder) if f.is_dir()]
for subfolder in subfolders:
update_mod(subfolder + "/")
else:
update_mod(folder)
print("Done.")
def run_all_subfolders(folder):
for modfolder in [f.path for f in os.scandir(folder) if f.is_dir()]:
update_folder(modfolder + "/")
main()

View File

@ -4,9 +4,8 @@ minetest.register_alias("castle:light", "castle_lighting:light")
minetest.register_alias("castle:chandelier", "castle_lighting:chandelier")
minetest.register_alias("castle:chandelier_chain", "castle_lighting:chandelier_chain")
-- internationalization boilerplate
local MP = minetest.get_modpath(minetest.get_current_modname())
local S, NS = dofile(MP.."/intllib.lua")
local S = minetest.get_translator(minetest.get_current_modname())
castle_lighting = {}

View File

@ -1,45 +0,0 @@
-- Fallback functions for when `intllib` is not installed.
-- Code released under Unlicense <http://unlicense.org>.
-- Get the latest version of this file at:
-- https://raw.githubusercontent.com/minetest-mods/intllib/master/lib/intllib.lua
local function format(str, ...)
local args = { ... }
local function repl(escape, open, num, close)
if escape == "" then
local replacement = tostring(args[tonumber(num)])
if open == "" then
replacement = replacement..close
end
return replacement
else
return "@"..open..num..close
end
end
return (str:gsub("(@?)@(%(?)(%d+)(%)?)", repl))
end
local gettext, ngettext
if minetest.get_modpath("intllib") then
if intllib.make_gettext_pair then
-- New method using gettext.
gettext, ngettext = intllib.make_gettext_pair()
else
-- Old method using text files.
gettext = intllib.Getter()
end
end
-- Fill in missing functions.
gettext = gettext or function(msgid, ...)
return format(msgid, ...)
end
ngettext = ngettext or function(msgid, msgid_plural, n, ...)
return format(n==1 and msgid or msgid_plural, ...)
end
return gettext, ngettext

View File

@ -0,0 +1,18 @@
# textdomain: castle_lighting
### brasier.lua ###
@1 Brasier=Brasero de @1
A brasier for producing copious amounts of light and heat.=Un brasero para producir grandes cantidades de luz y calor.
Floor Brasier=Brasero con Patas
Stonebrick=Ladrillo de piedra
To ignite the brasier place a flammable fuel in its inventory slot. A lump of coal will burn for about half an hour.=Para encender el brasero coloque un combustible inflamable en su ranura de inventario. Un trozo de carbón arderá durante media hora.
### init.lua ###
Chandelier=Candelero
Chandelier Chain=Colgante de Candelero
Light Block=Bloque de luz

View File

@ -0,0 +1,18 @@
# textdomain: castle_lighting
### brasier.lua ###
@1 Brasier=Braciere di @1
A brasier for producing copious amounts of light and heat.=Un braciere per produrre grandi quantità di luce e calore.
Floor Brasier=Braciere da pavimento
Stonebrick=Mattoni di pietra
To ignite the brasier place a flammable fuel in its inventory slot. A lump of coal will burn for about half an hour.=Per accendere il braciere mettete del combustibile nella sua casella di inventario. Un grumo di carbone brucerà per circa mezz'ora.
### init.lua ###
Chandelier=Candeliere
Chandelier Chain=Catena per candeliere
Light Block=Lampada

View File

@ -1,54 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-01 23:52-0700\n"
"PO-Revision-Date: 2017-04-28 12:40-0400\n"
"Last-Translator: Carlos Barraza\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: Español\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: brasier.lua:7
msgid "A brasier for producing copious amounts of light and heat."
msgstr "Un brasero para producir grandes cantidades de luz y calor."
#: brasier.lua:8
msgid ""
"To ignite the brasier place a flammable fuel in its inventory slot. A lump "
"of coal will burn for about half an hour."
msgstr ""
"Para encender el brasero coloque un combustible inflamable en su ranura de "
"inventario. Un trozo de carbón arderá durante media hora."
#: brasier.lua:107
msgid "Floor Brasier"
msgstr "Brasero con Patas"
#: brasier.lua:156
msgid "Stonebrick"
msgstr "Ladrillo de piedra"
#: brasier.lua:228
msgid "@1 Brasier"
msgstr "Brasero de @1"
#: init.lua:17
msgid "Light Block"
msgstr "Bloque de luz"
#: init.lua:37
msgid "Chandelier"
msgstr "Candelero"
#: init.lua:76
msgid "Chandelier Chain"
msgstr "Colgante de Candelero"

View File

@ -1,55 +0,0 @@
# ITALIAN LOCALE FILE FOR THE CASTLE LIGHTING MODULE
# Copyright (C) 2017 Philipbenr And DanDuncombe
# This file is distributed under the same license as the CASTLE LIGHTING package.
# Hamlet <h4mlet@riseup.net>, 2017.
#
msgid ""
msgstr ""
"Project-Id-Version: Castle Lighting\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-01 23:52-0700\n"
"PO-Revision-Date: 2017-09-10 22:00+0100\n"
"Last-Translator: H4mlet <h4mlet@riseup.net>\n"
"Language-Team: \n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 1.6.10\n"
#: brasier.lua:7
msgid "A brasier for producing copious amounts of light and heat."
msgstr "Un braciere per produrre grandi quantità di luce e calore."
#: brasier.lua:8
msgid ""
"To ignite the brasier place a flammable fuel in its inventory slot. A lump "
"of coal will burn for about half an hour."
msgstr ""
"Per accendere il braciere mettete del combustibile nella sua casella di "
"inventario. Un grumo di carbone brucerà per circa mezz'ora."
#: brasier.lua:107
msgid "Floor Brasier"
msgstr "Braciere da pavimento"
#: brasier.lua:156
msgid "Stonebrick"
msgstr "Mattoni di pietra"
#: brasier.lua:228
msgid "@1 Brasier"
msgstr "Braciere di @1"
#: init.lua:17
msgid "Light Block"
msgstr "Lampada"
#: init.lua:37
msgid "Chandelier"
msgstr "Candeliere"
#: init.lua:76
msgid "Chandelier Chain"
msgstr "Catena per candeliere"

View File

@ -1,52 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-01 23:52-0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: brasier.lua:7
msgid "A brasier for producing copious amounts of light and heat."
msgstr ""
#: brasier.lua:8
msgid ""
"To ignite the brasier place a flammable fuel in its inventory slot. A lump "
"of coal will burn for about half an hour."
msgstr ""
#: brasier.lua:107
msgid "Floor Brasier"
msgstr ""
#: brasier.lua:156
msgid "Stonebrick"
msgstr ""
#: brasier.lua:228
msgid "@1 Brasier"
msgstr ""
#: init.lua:17
msgid "Light Block"
msgstr ""
#: init.lua:37
msgid "Chandelier"
msgstr ""
#: init.lua:76
msgid "Chandelier Chain"
msgstr ""

View File

@ -0,0 +1,18 @@
# textdomain: castle_lighting
### brasier.lua ###
@1 Brasier=
A brasier for producing copious amounts of light and heat.=
Floor Brasier=
Stonebrick=
To ignite the brasier place a flammable fuel in its inventory slot. A lump of coal will burn for about half an hour.=
### init.lua ###
Chandelier=
Chandelier Chain=
Light Block=

View File

@ -1 +1,4 @@
name = castle_lighting
depends = default
optional_depends = fire, castle_masonry, hopper, doc
description = This mod contains medieval castle lighting solutions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 360 B

After

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 B

After

Width:  |  Height:  |  Size: 157 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 316 B

After

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 771 B

After

Width:  |  Height:  |  Size: 755 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 B

After

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 465 B

After

Width:  |  Height:  |  Size: 460 B

View File

@ -1,9 +1,7 @@
minetest.register_alias("darkage:box", "castle_storage:crate")
minetest.register_alias("castle:crate", "castle_storage:crate")
-- internationalization boilerplate
local MP = minetest.get_modpath(minetest.get_current_modname())
local S, NS = dofile(MP.."/intllib.lua")
local S = minetest.get_translator(minetest.get_current_modname())
minetest.register_node("castle_storage:crate", {
description = S("Crate"),

View File

@ -1,3 +1,2 @@
default
intllib?
hopper?

418
castle_storage/i18n.py Normal file
View File

@ -0,0 +1,418 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Script to generate the template file and update the translation files.
# Copy the script into the mod or modpack root folder and run it there.
#
# Copyright (C) 2019 Joachim Stolberg, 2020 FaceDeer, 2020 Louis Royer
# LGPLv2.1+
from __future__ import print_function
import os, fnmatch, re, shutil, errno
from sys import argv as _argv
# Running params
params = {"recursive": False,
"help": False,
"mods": False,
"verbose": False,
"folders": []
}
# Available CLI options
options = {"recursive": ['--recursive', '-r'],
"help": ['--help', '-h'],
"mods": ['--installed-mods'],
"verbose": ['--verbose', '-v']
}
# Strings longer than this will have extra space added between
# them in the translation files to make it easier to distinguish their
# beginnings and endings at a glance
doublespace_threshold = 60
def set_params_folders(tab: list):
'''Initialize params["folders"] from CLI arguments.'''
# Discarding argument 0 (tool name)
for param in tab[1:]:
stop_param = False
for option in options:
if param in options[option]:
stop_param = True
break
if not stop_param:
params["folders"].append(os.path.abspath(param))
def set_params(tab: list):
'''Initialize params from CLI arguments.'''
for option in options:
for option_name in options[option]:
if option_name in tab:
params[option] = True
break
def print_help(name):
'''Prints some help message.'''
print(f'''SYNOPSIS
{name} [OPTIONS] [PATHS...]
DESCRIPTION
{', '.join(options["help"])}
prints this help message
{', '.join(options["recursive"])}
run on all subfolders of paths given
{', '.join(options["mods"])}
run on locally installed modules
{', '.join(options["verbose"])}
add output information
''')
def main():
'''Main function'''
set_params(_argv)
set_params_folders(_argv)
if params["help"]:
print_help(_argv[0])
elif params["recursive"] and params["mods"]:
print("Option --installed-mods is incompatible with --recursive")
else:
# Add recursivity message
print("Running ", end='')
if params["recursive"]:
print("recursively ", end='')
# Running
if params["mods"]:
print(f"on all locally installed modules in {os.path.abspath('~/.minetest/mods/')}")
run_all_subfolders("~/.minetest/mods")
elif len(params["folders"]) >= 2:
print("on folder list:", params["folders"])
for f in params["folders"]:
if params["recursive"]:
run_all_subfolders(f)
else:
update_folder(f)
elif len(params["folders"]) == 1:
print("on folder", params["folders"][0])
if params["recursive"]:
run_all_subfolders(params["folders"][0])
else:
update_folder(params["folders"][0])
else:
print("on folder", os.path.abspath("./"))
if params["recursive"]:
run_all_subfolders(os.path.abspath("./"))
else:
update_folder(os.path.abspath("./"))
#group 2 will be the string, groups 1 and 3 will be the delimiters (" or ')
#See https://stackoverflow.com/questions/46967465/regex-match-text-in-either-single-or-double-quote
pattern_lua = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL)
pattern_lua_bracketed = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*\[\[(.*?)\]\][\s,\)]', re.DOTALL)
# Handles "concatenation" .. " of strings"
pattern_concat = re.compile(r'["\'][\s]*\.\.[\s]*["\']', re.DOTALL)
pattern_tr = re.compile(r'(.+?[^@])=(.*)')
pattern_name = re.compile(r'^name[ ]*=[ ]*([^ \n]*)')
pattern_tr_filename = re.compile(r'\.tr$')
pattern_po_language_code = re.compile(r'(.*)\.po$')
#attempt to read the mod's name from the mod.conf file. Returns None on failure
def get_modname(folder):
try:
with open(os.path.join(folder, "mod.conf"), "r", encoding='utf-8') as mod_conf:
for line in mod_conf:
match = pattern_name.match(line)
if match:
return match.group(1)
except FileNotFoundError:
pass
return None
#If there are already .tr files in /locale, returns a list of their names
def get_existing_tr_files(folder):
out = []
for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
for name in files:
if pattern_tr_filename.search(name):
out.append(name)
return out
# A series of search and replaces that massage a .po file's contents into
# a .tr file's equivalent
def process_po_file(text):
# The first three items are for unused matches
text = re.sub(r'#~ msgid "', "", text)
text = re.sub(r'"\n#~ msgstr ""\n"', "=", text)
text = re.sub(r'"\n#~ msgstr "', "=", text)
# comment lines
text = re.sub(r'#.*\n', "", text)
# converting msg pairs into "=" pairs
text = re.sub(r'msgid "', "", text)
text = re.sub(r'"\nmsgstr ""\n"', "=", text)
text = re.sub(r'"\nmsgstr "', "=", text)
# various line breaks and escape codes
text = re.sub(r'"\n"', "", text)
text = re.sub(r'"\n', "\n", text)
text = re.sub(r'\\"', '"', text)
text = re.sub(r'\\n', '@n', text)
# remove header text
text = re.sub(r'=Project-Id-Version:.*\n', "", text)
# remove double-spaced lines
text = re.sub(r'\n\n', '\n', text)
return text
# Go through existing .po files and, if a .tr file for that language
# *doesn't* exist, convert it and create it.
# The .tr file that results will subsequently be reprocessed so
# any "no longer used" strings will be preserved.
# Note that "fuzzy" tags will be lost in this process.
def process_po_files(folder, modname):
for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
for name in files:
code_match = pattern_po_language_code.match(name)
if code_match == None:
continue
language_code = code_match.group(1)
tr_name = modname + "." + language_code + ".tr"
tr_file = os.path.join(root, tr_name)
if os.path.exists(tr_file):
if params["verbose"]:
print(f"{tr_name} already exists, ignoring {name}")
continue
fname = os.path.join(root, name)
with open(fname, "r", encoding='utf-8') as po_file:
if params["verbose"]:
print(f"Importing translations from {name}")
text = process_po_file(po_file.read())
with open(tr_file, "wt", encoding='utf-8') as tr_out:
tr_out.write(text)
# from https://stackoverflow.com/questions/600268/mkdir-p-functionality-in-python/600612#600612
# Creates a directory if it doesn't exist, silently does
# nothing if it already exists
def mkdir_p(path):
try:
os.makedirs(path)
except OSError as exc: # Python >2.5
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else: raise
# Converts the template dictionary to a text to be written as a file
# dKeyStrings is a dictionary of localized string to source file sets
# dOld is a dictionary of existing translations and comments from
# the previous version of this text
def strings_to_text(dkeyStrings, dOld, mod_name):
lOut = [f"# textdomain: {mod_name}\n"]
dGroupedBySource = {}
for key in dkeyStrings:
sourceList = list(dkeyStrings[key])
sourceList.sort()
sourceString = "\n".join(sourceList)
listForSource = dGroupedBySource.get(sourceString, [])
listForSource.append(key)
dGroupedBySource[sourceString] = listForSource
lSourceKeys = list(dGroupedBySource.keys())
lSourceKeys.sort()
for source in lSourceKeys:
localizedStrings = dGroupedBySource[source]
localizedStrings.sort()
lOut.append("")
lOut.append(source)
lOut.append("")
for localizedString in localizedStrings:
val = dOld.get(localizedString, {})
translation = val.get("translation", "")
comment = val.get("comment")
if len(localizedString) > doublespace_threshold and not lOut[-1] == "":
lOut.append("")
if comment != None:
lOut.append(comment)
lOut.append(f"{localizedString}={translation}")
if len(localizedString) > doublespace_threshold:
lOut.append("")
unusedExist = False
for key in dOld:
if key not in dkeyStrings:
val = dOld[key]
translation = val.get("translation")
comment = val.get("comment")
# only keep an unused translation if there was translated
# text or a comment associated with it
if translation != None and (translation != "" or comment):
if not unusedExist:
unusedExist = True
lOut.append("\n\n##### not used anymore #####\n")
if len(key) > doublespace_threshold and not lOut[-1] == "":
lOut.append("")
if comment != None:
lOut.append(comment)
lOut.append(f"{key}={translation}")
if len(key) > doublespace_threshold:
lOut.append("")
return "\n".join(lOut) + '\n'
# Writes a template.txt file
# dkeyStrings is the dictionary returned by generate_template
def write_template(templ_file, dkeyStrings, mod_name):
# read existing template file to preserve comments
existing_template = import_tr_file(templ_file)
text = strings_to_text(dkeyStrings, existing_template[0], mod_name)
mkdir_p(os.path.dirname(templ_file))
with open(templ_file, "wt", encoding='utf-8') as template_file:
template_file.write(text)
# Gets all translatable strings from a lua file
def read_lua_file_strings(lua_file):
lOut = []
with open(lua_file, encoding='utf-8') as text_file:
text = text_file.read()
#TODO remove comments here
text = re.sub(pattern_concat, "", text)
strings = []
for s in pattern_lua.findall(text):
strings.append(s[1])
for s in pattern_lua_bracketed.findall(text):
strings.append(s)
for s in strings:
s = re.sub(r'"\.\.\s+"', "", s)
s = re.sub("@[^@=0-9]", "@@", s)
s = s.replace('\\"', '"')
s = s.replace("\\'", "'")
s = s.replace("\n", "@n")
s = s.replace("\\n", "@n")
s = s.replace("=", "@=")
lOut.append(s)
return lOut
# Gets strings from an existing translation file
# returns both a dictionary of translations
# and the full original source text so that the new text
# can be compared to it for changes.
def import_tr_file(tr_file):
dOut = {}
text = None
if os.path.exists(tr_file):
with open(tr_file, "r", encoding='utf-8') as existing_file :
# save the full text to allow for comparison
# of the old version with the new output
text = existing_file.read()
existing_file.seek(0)
# a running record of the current comment block
# we're inside, to allow preceeding multi-line comments
# to be retained for a translation line
latest_comment_block = None
for line in existing_file.readlines():
line = line.rstrip('\n')
if line[:3] == "###":
# Reset comment block if we hit a header
latest_comment_block = None
continue
if line[:1] == "#":
# Save the comment we're inside
if not latest_comment_block:
latest_comment_block = line
else:
latest_comment_block = latest_comment_block + "\n" + line
continue
match = pattern_tr.match(line)
if match:
# this line is a translated line
outval = {}
outval["translation"] = match.group(2)
if latest_comment_block:
# if there was a comment, record that.
outval["comment"] = latest_comment_block
latest_comment_block = None
dOut[match.group(1)] = outval
return (dOut, text)
# Walks all lua files in the mod folder, collects translatable strings,
# and writes it to a template.txt file
# Returns a dictionary of localized strings to source file sets
# that can be used with the strings_to_text function.
def generate_template(folder, mod_name):
dOut = {}
for root, dirs, files in os.walk(folder):
for name in files:
if fnmatch.fnmatch(name, "*.lua"):
fname = os.path.join(root, name)
found = read_lua_file_strings(fname)
if params["verbose"]:
print(f"{fname}: {str(len(found))} translatable strings")
for s in found:
sources = dOut.get(s, set())
sources.add(f"### {os.path.basename(fname)} ###")
dOut[s] = sources
if len(dOut) == 0:
return None
templ_file = os.path.join(folder, "locale/template.txt")
write_template(templ_file, dOut, mod_name)
return dOut
# Updates an existing .tr file, copying the old one to a ".old" file
# if any changes have happened
# dNew is the data used to generate the template, it has all the
# currently-existing localized strings
def update_tr_file(dNew, mod_name, tr_file):
if params["verbose"]:
print(f"updating {tr_file}")
tr_import = import_tr_file(tr_file)
dOld = tr_import[0]
textOld = tr_import[1]
textNew = strings_to_text(dNew, dOld, mod_name)
if textOld and textOld != textNew:
print(f"{tr_file} has changed.")
shutil.copyfile(tr_file, f"{tr_file}.old")
with open(tr_file, "w", encoding='utf-8') as new_tr_file:
new_tr_file.write(textNew)
# Updates translation files for the mod in the given folder
def update_mod(folder):
modname = get_modname(folder)
if modname is not None:
process_po_files(folder, modname)
print(f"Updating translations for {modname}")
data = generate_template(folder, modname)
if data == None:
print(f"No translatable strings found in {modname}")
else:
for tr_file in get_existing_tr_files(folder):
update_tr_file(data, modname, os.path.join(folder, "locale/", tr_file))
else:
print("Unable to find modname in folder " + folder)
# Determines if the folder being pointed to is a mod or a mod pack
# and then runs update_mod accordingly
def update_folder(folder):
is_modpack = os.path.exists(os.path.join(folder, "modpack.txt")) or os.path.exists(os.path.join(folder, "modpack.conf"))
if is_modpack:
subfolders = [f.path for f in os.scandir(folder) if f.is_dir()]
for subfolder in subfolders:
update_mod(subfolder + "/")
else:
update_mod(folder)
print("Done.")
def run_all_subfolders(folder):
for modfolder in [f.path for f in os.scandir(folder) if f.is_dir()]:
update_folder(modfolder + "/")
main()

View File

@ -1,45 +0,0 @@
-- Fallback functions for when `intllib` is not installed.
-- Code released under Unlicense <http://unlicense.org>.
-- Get the latest version of this file at:
-- https://raw.githubusercontent.com/minetest-mods/intllib/master/lib/intllib.lua
local function format(str, ...)
local args = { ... }
local function repl(escape, open, num, close)
if escape == "" then
local replacement = tostring(args[tonumber(num)])
if open == "" then
replacement = replacement..close
end
return replacement
else
return "@"..open..num..close
end
end
return (str:gsub("(@?)@(%(?)(%d+)(%)?)", repl))
end
local gettext, ngettext
if minetest.get_modpath("intllib") then
if intllib.make_gettext_pair then
-- New method using gettext.
gettext, ngettext = intllib.make_gettext_pair()
else
-- Old method using text files.
gettext = intllib.Getter()
end
end
-- Fill in missing functions.
gettext = gettext or function(msgid, ...)
return format(msgid, ...)
end
ngettext = ngettext or function(msgid, msgid_plural, n, ...)
return format(n==1 and msgid or msgid_plural, ...)
end
return gettext, ngettext

View File

@ -1,8 +1,6 @@
minetest.register_alias("castle:ironbound_chest", "castle_storage:ironbound_chest")
-- internationalization boilerplate
local MP = minetest.get_modpath(minetest.get_current_modname())
local S, NS = dofile(MP.."/intllib.lua")
local S = minetest.get_translator(minetest.get_current_modname())
local get_ironbound_chest_formspec = function(pos)
local spos = pos.x .. "," .. pos.y .. "," ..pos.z

View File

@ -0,0 +1,21 @@
# textdomain: castle_storage
### crate.lua ###
@1 moves stuff in crate at @2=@1 mette delle cose nella cassa alle coordinate @2
@1 moves stuff to crate at @2=@1 prende delle cose dalla cassa alle coordinate @2
Crate=Cassa
### crate.lua ###
### ironbound_chest.lua ###
@1 takes stuff from locked chest at @2=@1 prende delle cose dal baule chiuso a chiave alle coordinate @2
### ironbound_chest.lua ###
@1 moves stuff in locked chest at @2=@1 sposta delle cose nel baule chiuso a chiave alle coordinate @2
@1 moves stuff to locked chest at @2=@1 mette delle cose nel baule chiuso a chiave alle coordinate @2
@1 tried to access a locked chest belonging to @2 at @3=@1 ha tentato di aprire un baule chiuso a chiave di @2 alle coordinate @3
Ironbound Chest=Baule rinforzato col ferro
Ironbound Chest (owned by @1)=Baule rinforzato col ferro (di @1)

View File

@ -1,56 +0,0 @@
# ITALIAN LOCALE FILE FOR THE CASTLE STORAGE MODULE
# Copyright (C) 2017 Philipbenr And DanDuncombe
# This file is distributed under the same license as the CASTLE STORAGE package.
# Hamlet <h4mlet@riseup.net>, 2017.
#
msgid ""
msgstr ""
"Project-Id-Version: Castle Storage\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-02-25 19:18-0700\n"
"PO-Revision-Date: 2017-09-10 22:46+0100\n"
"Last-Translator: H4mlet <h4mlet@riseup.net>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Language: it\n"
"X-Generator: Poedit 1.6.10\n"
#: crate.lua:9 crate.lua:24
msgid "Crate"
msgstr "Cassa"
#: crate.lua:34
msgid "@1 moves stuff in crate at @2"
msgstr "@1 mette delle cose nella cassa alle coordinate @2"
#: crate.lua:37
msgid "@1 moves stuff to crate at @2"
msgstr "@1 prende delle cose dalla cassa alle coordinate @2"
#: crate.lua:40 ironbound_chest.lua:113
msgid "@1 takes stuff from locked chest at @2"
msgstr "@1 prende delle cose dal baule chiuso a chiave alle coordinate @2"
#: ironbound_chest.lua:35 ironbound_chest.lua:69
msgid "Ironbound Chest"
msgstr "Baule rinforzato col ferro"
#: ironbound_chest.lua:65
msgid "Ironbound Chest (owned by @1)"
msgstr "Baule rinforzato col ferro (di @1)"
#: ironbound_chest.lua:82 ironbound_chest.lua:91 ironbound_chest.lua:100
msgid "@1 tried to access a locked chest belonging to @2 at @3"
msgstr ""
"@1 ha tentato di aprire un baule chiuso a chiave di @2 alle coordinate @3"
#: ironbound_chest.lua:107
msgid "@1 moves stuff in locked chest at @2"
msgstr "@1 sposta delle cose nel baule chiuso a chiave alle coordinate @2"
#: ironbound_chest.lua:110
msgid "@1 moves stuff to locked chest at @2"
msgstr "@1 mette delle cose nel baule chiuso a chiave alle coordinate @2"

View File

@ -1,53 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-02-25 19:18-0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: crate.lua:9 crate.lua:24
msgid "Crate"
msgstr ""
#: crate.lua:34
msgid "@1 moves stuff in crate at @2"
msgstr ""
#: crate.lua:37
msgid "@1 moves stuff to crate at @2"
msgstr ""
#: crate.lua:40 ironbound_chest.lua:113
msgid "@1 takes stuff from locked chest at @2"
msgstr ""
#: ironbound_chest.lua:35 ironbound_chest.lua:69
msgid "Ironbound Chest"
msgstr ""
#: ironbound_chest.lua:65
msgid "Ironbound Chest (owned by @1)"
msgstr ""
#: ironbound_chest.lua:82 ironbound_chest.lua:91 ironbound_chest.lua:100
msgid "@1 tried to access a locked chest belonging to @2 at @3"
msgstr ""
#: ironbound_chest.lua:107
msgid "@1 moves stuff in locked chest at @2"
msgstr ""
#: ironbound_chest.lua:110
msgid "@1 moves stuff to locked chest at @2"
msgstr ""

View File

@ -0,0 +1,21 @@
# textdomain: castle_storage
### crate.lua ###
@1 moves stuff in crate at @2=
@1 moves stuff to crate at @2=
Crate=
### crate.lua ###
### ironbound_chest.lua ###
@1 takes stuff from locked chest at @2=
### ironbound_chest.lua ###
@1 moves stuff in locked chest at @2=
@1 moves stuff to locked chest at @2=
@1 tried to access a locked chest belonging to @2 at @3=
Ironbound Chest=
Ironbound Chest (owned by @1)=

View File

@ -1 +1,4 @@
name = castle_storage
name = castle_storage
depends = default
optional_depends = hopper
description = This mod contains storage containers one might find contained in a castle.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 374 B

After

Width:  |  Height:  |  Size: 365 B

View File

@ -1,6 +1,7 @@
-- internationalization boilerplate
local MP = minetest.get_modpath(minetest.get_current_modname())
local S, NS = dofile(MP.."/intllib.lua")
-- Used for localization, choose either built-in or intllib.
local S = minetest.get_translator(minetest.get_current_modname())
minetest.register_alias("castle:battleaxe", "castle_weapons:battleaxe")

View File

@ -18,9 +18,7 @@ minetest.register_alias("castle:bolt", "castle_weapons:crossbow_bolt")
minetest.register_alias("castle:crossbow_bolt", "castle_weapons:crossbow_bolt")
minetest.register_alias("castle:crossbow_loaded", "castle_weapons:crossbow_loaded")
-- internationalization boilerplate
local MP = minetest.get_modpath(minetest.get_current_modname())
local S, NS = dofile(MP.."/intllib.lua")
local S = minetest.get_translator(minetest.get_current_modname())
local crossbow={}
@ -458,4 +456,4 @@ minetest.register_craft({
recipe = {
{'default:stick', 'default:stick', 'default:steel_ingot'},
}
})
})

418
castle_weapons/i18n.py Normal file
View File

@ -0,0 +1,418 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Script to generate the template file and update the translation files.
# Copy the script into the mod or modpack root folder and run it there.
#
# Copyright (C) 2019 Joachim Stolberg, 2020 FaceDeer, 2020 Louis Royer
# LGPLv2.1+
from __future__ import print_function
import os, fnmatch, re, shutil, errno
from sys import argv as _argv
# Running params
params = {"recursive": False,
"help": False,
"mods": False,
"verbose": False,
"folders": []
}
# Available CLI options
options = {"recursive": ['--recursive', '-r'],
"help": ['--help', '-h'],
"mods": ['--installed-mods'],
"verbose": ['--verbose', '-v']
}
# Strings longer than this will have extra space added between
# them in the translation files to make it easier to distinguish their
# beginnings and endings at a glance
doublespace_threshold = 60
def set_params_folders(tab: list):
'''Initialize params["folders"] from CLI arguments.'''
# Discarding argument 0 (tool name)
for param in tab[1:]:
stop_param = False
for option in options:
if param in options[option]:
stop_param = True
break
if not stop_param:
params["folders"].append(os.path.abspath(param))
def set_params(tab: list):
'''Initialize params from CLI arguments.'''
for option in options:
for option_name in options[option]:
if option_name in tab:
params[option] = True
break
def print_help(name):
'''Prints some help message.'''
print(f'''SYNOPSIS
{name} [OPTIONS] [PATHS...]
DESCRIPTION
{', '.join(options["help"])}
prints this help message
{', '.join(options["recursive"])}
run on all subfolders of paths given
{', '.join(options["mods"])}
run on locally installed modules
{', '.join(options["verbose"])}
add output information
''')
def main():
'''Main function'''
set_params(_argv)
set_params_folders(_argv)
if params["help"]:
print_help(_argv[0])
elif params["recursive"] and params["mods"]:
print("Option --installed-mods is incompatible with --recursive")
else:
# Add recursivity message
print("Running ", end='')
if params["recursive"]:
print("recursively ", end='')
# Running
if params["mods"]:
print(f"on all locally installed modules in {os.path.abspath('~/.minetest/mods/')}")
run_all_subfolders("~/.minetest/mods")
elif len(params["folders"]) >= 2:
print("on folder list:", params["folders"])
for f in params["folders"]:
if params["recursive"]:
run_all_subfolders(f)
else:
update_folder(f)
elif len(params["folders"]) == 1:
print("on folder", params["folders"][0])
if params["recursive"]:
run_all_subfolders(params["folders"][0])
else:
update_folder(params["folders"][0])
else:
print("on folder", os.path.abspath("./"))
if params["recursive"]:
run_all_subfolders(os.path.abspath("./"))
else:
update_folder(os.path.abspath("./"))
#group 2 will be the string, groups 1 and 3 will be the delimiters (" or ')
#See https://stackoverflow.com/questions/46967465/regex-match-text-in-either-single-or-double-quote
pattern_lua = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL)
pattern_lua_bracketed = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*\[\[(.*?)\]\][\s,\)]', re.DOTALL)
# Handles "concatenation" .. " of strings"
pattern_concat = re.compile(r'["\'][\s]*\.\.[\s]*["\']', re.DOTALL)
pattern_tr = re.compile(r'(.+?[^@])=(.*)')
pattern_name = re.compile(r'^name[ ]*=[ ]*([^ \n]*)')
pattern_tr_filename = re.compile(r'\.tr$')
pattern_po_language_code = re.compile(r'(.*)\.po$')
#attempt to read the mod's name from the mod.conf file. Returns None on failure
def get_modname(folder):
try:
with open(os.path.join(folder, "mod.conf"), "r", encoding='utf-8') as mod_conf:
for line in mod_conf:
match = pattern_name.match(line)
if match:
return match.group(1)
except FileNotFoundError:
pass
return None
#If there are already .tr files in /locale, returns a list of their names
def get_existing_tr_files(folder):
out = []
for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
for name in files:
if pattern_tr_filename.search(name):
out.append(name)
return out
# A series of search and replaces that massage a .po file's contents into
# a .tr file's equivalent
def process_po_file(text):
# The first three items are for unused matches
text = re.sub(r'#~ msgid "', "", text)
text = re.sub(r'"\n#~ msgstr ""\n"', "=", text)
text = re.sub(r'"\n#~ msgstr "', "=", text)
# comment lines
text = re.sub(r'#.*\n', "", text)
# converting msg pairs into "=" pairs
text = re.sub(r'msgid "', "", text)
text = re.sub(r'"\nmsgstr ""\n"', "=", text)
text = re.sub(r'"\nmsgstr "', "=", text)
# various line breaks and escape codes
text = re.sub(r'"\n"', "", text)
text = re.sub(r'"\n', "\n", text)
text = re.sub(r'\\"', '"', text)
text = re.sub(r'\\n', '@n', text)
# remove header text
text = re.sub(r'=Project-Id-Version:.*\n', "", text)
# remove double-spaced lines
text = re.sub(r'\n\n', '\n', text)
return text
# Go through existing .po files and, if a .tr file for that language
# *doesn't* exist, convert it and create it.
# The .tr file that results will subsequently be reprocessed so
# any "no longer used" strings will be preserved.
# Note that "fuzzy" tags will be lost in this process.
def process_po_files(folder, modname):
for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
for name in files:
code_match = pattern_po_language_code.match(name)
if code_match == None:
continue
language_code = code_match.group(1)
tr_name = modname + "." + language_code + ".tr"
tr_file = os.path.join(root, tr_name)
if os.path.exists(tr_file):
if params["verbose"]:
print(f"{tr_name} already exists, ignoring {name}")
continue
fname = os.path.join(root, name)
with open(fname, "r", encoding='utf-8') as po_file:
if params["verbose"]:
print(f"Importing translations from {name}")
text = process_po_file(po_file.read())
with open(tr_file, "wt", encoding='utf-8') as tr_out:
tr_out.write(text)
# from https://stackoverflow.com/questions/600268/mkdir-p-functionality-in-python/600612#600612
# Creates a directory if it doesn't exist, silently does
# nothing if it already exists
def mkdir_p(path):
try:
os.makedirs(path)
except OSError as exc: # Python >2.5
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else: raise
# Converts the template dictionary to a text to be written as a file
# dKeyStrings is a dictionary of localized string to source file sets
# dOld is a dictionary of existing translations and comments from
# the previous version of this text
def strings_to_text(dkeyStrings, dOld, mod_name):
lOut = [f"# textdomain: {mod_name}\n"]
dGroupedBySource = {}
for key in dkeyStrings:
sourceList = list(dkeyStrings[key])
sourceList.sort()
sourceString = "\n".join(sourceList)
listForSource = dGroupedBySource.get(sourceString, [])
listForSource.append(key)
dGroupedBySource[sourceString] = listForSource
lSourceKeys = list(dGroupedBySource.keys())
lSourceKeys.sort()
for source in lSourceKeys:
localizedStrings = dGroupedBySource[source]
localizedStrings.sort()
lOut.append("")
lOut.append(source)
lOut.append("")
for localizedString in localizedStrings:
val = dOld.get(localizedString, {})
translation = val.get("translation", "")
comment = val.get("comment")
if len(localizedString) > doublespace_threshold and not lOut[-1] == "":
lOut.append("")
if comment != None:
lOut.append(comment)
lOut.append(f"{localizedString}={translation}")
if len(localizedString) > doublespace_threshold:
lOut.append("")
unusedExist = False
for key in dOld:
if key not in dkeyStrings:
val = dOld[key]
translation = val.get("translation")
comment = val.get("comment")
# only keep an unused translation if there was translated
# text or a comment associated with it
if translation != None and (translation != "" or comment):
if not unusedExist:
unusedExist = True
lOut.append("\n\n##### not used anymore #####\n")
if len(key) > doublespace_threshold and not lOut[-1] == "":
lOut.append("")
if comment != None:
lOut.append(comment)
lOut.append(f"{key}={translation}")
if len(key) > doublespace_threshold:
lOut.append("")
return "\n".join(lOut) + '\n'
# Writes a template.txt file
# dkeyStrings is the dictionary returned by generate_template
def write_template(templ_file, dkeyStrings, mod_name):
# read existing template file to preserve comments
existing_template = import_tr_file(templ_file)
text = strings_to_text(dkeyStrings, existing_template[0], mod_name)
mkdir_p(os.path.dirname(templ_file))
with open(templ_file, "wt", encoding='utf-8') as template_file:
template_file.write(text)
# Gets all translatable strings from a lua file
def read_lua_file_strings(lua_file):
lOut = []
with open(lua_file, encoding='utf-8') as text_file:
text = text_file.read()
#TODO remove comments here
text = re.sub(pattern_concat, "", text)
strings = []
for s in pattern_lua.findall(text):
strings.append(s[1])
for s in pattern_lua_bracketed.findall(text):
strings.append(s)
for s in strings:
s = re.sub(r'"\.\.\s+"', "", s)
s = re.sub("@[^@=0-9]", "@@", s)
s = s.replace('\\"', '"')
s = s.replace("\\'", "'")
s = s.replace("\n", "@n")
s = s.replace("\\n", "@n")
s = s.replace("=", "@=")
lOut.append(s)
return lOut
# Gets strings from an existing translation file
# returns both a dictionary of translations
# and the full original source text so that the new text
# can be compared to it for changes.
def import_tr_file(tr_file):
dOut = {}
text = None
if os.path.exists(tr_file):
with open(tr_file, "r", encoding='utf-8') as existing_file :
# save the full text to allow for comparison
# of the old version with the new output
text = existing_file.read()
existing_file.seek(0)
# a running record of the current comment block
# we're inside, to allow preceeding multi-line comments
# to be retained for a translation line
latest_comment_block = None
for line in existing_file.readlines():
line = line.rstrip('\n')
if line[:3] == "###":
# Reset comment block if we hit a header
latest_comment_block = None
continue
if line[:1] == "#":
# Save the comment we're inside
if not latest_comment_block:
latest_comment_block = line
else:
latest_comment_block = latest_comment_block + "\n" + line
continue
match = pattern_tr.match(line)
if match:
# this line is a translated line
outval = {}
outval["translation"] = match.group(2)
if latest_comment_block:
# if there was a comment, record that.
outval["comment"] = latest_comment_block
latest_comment_block = None
dOut[match.group(1)] = outval
return (dOut, text)
# Walks all lua files in the mod folder, collects translatable strings,
# and writes it to a template.txt file
# Returns a dictionary of localized strings to source file sets
# that can be used with the strings_to_text function.
def generate_template(folder, mod_name):
dOut = {}
for root, dirs, files in os.walk(folder):
for name in files:
if fnmatch.fnmatch(name, "*.lua"):
fname = os.path.join(root, name)
found = read_lua_file_strings(fname)
if params["verbose"]:
print(f"{fname}: {str(len(found))} translatable strings")
for s in found:
sources = dOut.get(s, set())
sources.add(f"### {os.path.basename(fname)} ###")
dOut[s] = sources
if len(dOut) == 0:
return None
templ_file = os.path.join(folder, "locale/template.txt")
write_template(templ_file, dOut, mod_name)
return dOut
# Updates an existing .tr file, copying the old one to a ".old" file
# if any changes have happened
# dNew is the data used to generate the template, it has all the
# currently-existing localized strings
def update_tr_file(dNew, mod_name, tr_file):
if params["verbose"]:
print(f"updating {tr_file}")
tr_import = import_tr_file(tr_file)
dOld = tr_import[0]
textOld = tr_import[1]
textNew = strings_to_text(dNew, dOld, mod_name)
if textOld and textOld != textNew:
print(f"{tr_file} has changed.")
shutil.copyfile(tr_file, f"{tr_file}.old")
with open(tr_file, "w", encoding='utf-8') as new_tr_file:
new_tr_file.write(textNew)
# Updates translation files for the mod in the given folder
def update_mod(folder):
modname = get_modname(folder)
if modname is not None:
process_po_files(folder, modname)
print(f"Updating translations for {modname}")
data = generate_template(folder, modname)
if data == None:
print(f"No translatable strings found in {modname}")
else:
for tr_file in get_existing_tr_files(folder):
update_tr_file(data, modname, os.path.join(folder, "locale/", tr_file))
else:
print("Unable to find modname in folder " + folder)
# Determines if the folder being pointed to is a mod or a mod pack
# and then runs update_mod accordingly
def update_folder(folder):
is_modpack = os.path.exists(os.path.join(folder, "modpack.txt")) or os.path.exists(os.path.join(folder, "modpack.conf"))
if is_modpack:
subfolders = [f.path for f in os.scandir(folder) if f.is_dir()]
for subfolder in subfolders:
update_mod(subfolder + "/")
else:
update_mod(folder)
print("Done.")
def run_all_subfolders(folder):
for modfolder in [f.path for f in os.scandir(folder) if f.is_dir()]:
update_folder(modfolder + "/")
main()

View File

@ -1,45 +0,0 @@
-- Fallback functions for when `intllib` is not installed.
-- Code released under Unlicense <http://unlicense.org>.
-- Get the latest version of this file at:
-- https://raw.githubusercontent.com/minetest-mods/intllib/master/lib/intllib.lua
local function format(str, ...)
local args = { ... }
local function repl(escape, open, num, close)
if escape == "" then
local replacement = tostring(args[tonumber(num)])
if open == "" then
replacement = replacement..close
end
return replacement
else
return "@"..open..num..close
end
end
return (str:gsub("(@?)@(%(?)(%d+)(%)?)", repl))
end
local gettext, ngettext
if minetest.get_modpath("intllib") then
if intllib.make_gettext_pair then
-- New method using gettext.
gettext, ngettext = intllib.make_gettext_pair()
else
-- Old method using text files.
gettext = intllib.Getter()
end
end
-- Fill in missing functions.
gettext = gettext or function(msgid, ...)
return format(msgid, ...)
end
ngettext = ngettext or function(msgid, msgid_plural, n, ...)
return format(n==1 and msgid or msgid_plural, ...)
end
return gettext, ngettext

View File

@ -0,0 +1,12 @@
# textdomain: castle_weapons
### battleaxe.lua ###
# textdomain:castle_weapons
Battleaxe=Ascia da guerra
### crossbow.lua ###
Bolt=Quadrello
Crossbow=Balestra

View File

@ -1,31 +0,0 @@
# ITALIAN LOCALE FILE FOR THE CASTLE WEAPONS MODULE
# Copyright (C) 2017 Philipbenr And DanDuncombe
# This file is distributed under the same license as the CASTLE WEAPONS package.
# Hamlet <h4mlet@riseup.net>, 2017.
#
msgid ""
msgstr ""
"Project-Id-Version: Castle Weapons\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-02-25 19:35-0700\n"
"PO-Revision-Date: 2017-09-10 22:49+0100\n"
"Last-Translator: H4mlet <h4mlet@riseup.net>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Language: it\n"
"X-Generator: Poedit 1.6.10\n"
#: battleaxe.lua:8
msgid "Battleaxe"
msgstr "Ascia da guerra"
#: crossbow.lua:244
msgid "Bolt"
msgstr "Quadrello"
#: crossbow.lua:357 crossbow.lua:416
msgid "Crossbow"
msgstr "Balestra"

View File

@ -1,29 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-02-25 19:35-0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: battleaxe.lua:8
msgid "Battleaxe"
msgstr ""
#: crossbow.lua:244
msgid "Bolt"
msgstr ""
#: crossbow.lua:357 crossbow.lua:416
msgid "Crossbow"
msgstr ""

View File

@ -0,0 +1,12 @@
# textdomain: castle_weapons
### battleaxe.lua ###
# textdomain:castle_weapons
Battleaxe=
### crossbow.lua ###
Bolt=
Crossbow=

View File

@ -1 +1,3 @@
name = castle_weapons
description = Provides several medieval weapons for use around castles
depends = default

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 358 B

After

Width:  |  Height:  |  Size: 225 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 B

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

After

Width:  |  Height:  |  Size: 282 B

View File

@ -1,4 +0,0 @@
default
intllib?
hopper?
doc?

View File

@ -1 +0,0 @@
An auto-crafting bench

421
crafting_bench/i18n.py Normal file
View File

@ -0,0 +1,421 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Script to generate the template file and update the translation files.
# Copy the script into the mod or modpack root folder and run it there.
#
# Copyright (C) 2019 Joachim Stolberg, 2020 FaceDeer, 2020 Louis Royer
# LGPLv2.1+
#
# See https://github.com/minetest-tools/update_translations for
# potential future updates to this script.
from __future__ import print_function
import os, fnmatch, re, shutil, errno
from sys import argv as _argv
# Running params
params = {"recursive": False,
"help": False,
"mods": False,
"verbose": False,
"folders": []
}
# Available CLI options
options = {"recursive": ['--recursive', '-r'],
"help": ['--help', '-h'],
"mods": ['--installed-mods'],
"verbose": ['--verbose', '-v']
}
# Strings longer than this will have extra space added between
# them in the translation files to make it easier to distinguish their
# beginnings and endings at a glance
doublespace_threshold = 60
def set_params_folders(tab: list):
'''Initialize params["folders"] from CLI arguments.'''
# Discarding argument 0 (tool name)
for param in tab[1:]:
stop_param = False
for option in options:
if param in options[option]:
stop_param = True
break
if not stop_param:
params["folders"].append(os.path.abspath(param))
def set_params(tab: list):
'''Initialize params from CLI arguments.'''
for option in options:
for option_name in options[option]:
if option_name in tab:
params[option] = True
break
def print_help(name):
'''Prints some help message.'''
print(f'''SYNOPSIS
{name} [OPTIONS] [PATHS...]
DESCRIPTION
{', '.join(options["help"])}
prints this help message
{', '.join(options["recursive"])}
run on all subfolders of paths given
{', '.join(options["mods"])}
run on locally installed modules
{', '.join(options["verbose"])}
add output information
''')
def main():
'''Main function'''
set_params(_argv)
set_params_folders(_argv)
if params["help"]:
print_help(_argv[0])
elif params["recursive"] and params["mods"]:
print("Option --installed-mods is incompatible with --recursive")
else:
# Add recursivity message
print("Running ", end='')
if params["recursive"]:
print("recursively ", end='')
# Running
if params["mods"]:
print(f"on all locally installed modules in {os.path.abspath('~/.minetest/mods/')}")
run_all_subfolders("~/.minetest/mods")
elif len(params["folders"]) >= 2:
print("on folder list:", params["folders"])
for f in params["folders"]:
if params["recursive"]:
run_all_subfolders(f)
else:
update_folder(f)
elif len(params["folders"]) == 1:
print("on folder", params["folders"][0])
if params["recursive"]:
run_all_subfolders(params["folders"][0])
else:
update_folder(params["folders"][0])
else:
print("on folder", os.path.abspath("./"))
if params["recursive"]:
run_all_subfolders(os.path.abspath("./"))
else:
update_folder(os.path.abspath("./"))
#group 2 will be the string, groups 1 and 3 will be the delimiters (" or ')
#See https://stackoverflow.com/questions/46967465/regex-match-text-in-either-single-or-double-quote
pattern_lua = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL)
pattern_lua_bracketed = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*\[\[(.*?)\]\][\s,\)]', re.DOTALL)
# Handles "concatenation" .. " of strings"
pattern_concat = re.compile(r'["\'][\s]*\.\.[\s]*["\']', re.DOTALL)
pattern_tr = re.compile(r'(.+?[^@])=(.*)')
pattern_name = re.compile(r'^name[ ]*=[ ]*([^ \n]*)')
pattern_tr_filename = re.compile(r'\.tr$')
pattern_po_language_code = re.compile(r'(.*)\.po$')
#attempt to read the mod's name from the mod.conf file. Returns None on failure
def get_modname(folder):
try:
with open(os.path.join(folder, "mod.conf"), "r", encoding='utf-8') as mod_conf:
for line in mod_conf:
match = pattern_name.match(line)
if match:
return match.group(1)
except FileNotFoundError:
pass
return None
#If there are already .tr files in /locale, returns a list of their names
def get_existing_tr_files(folder):
out = []
for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
for name in files:
if pattern_tr_filename.search(name):
out.append(name)
return out
# A series of search and replaces that massage a .po file's contents into
# a .tr file's equivalent
def process_po_file(text):
# The first three items are for unused matches
text = re.sub(r'#~ msgid "', "", text)
text = re.sub(r'"\n#~ msgstr ""\n"', "=", text)
text = re.sub(r'"\n#~ msgstr "', "=", text)
# comment lines
text = re.sub(r'#.*\n', "", text)
# converting msg pairs into "=" pairs
text = re.sub(r'msgid "', "", text)
text = re.sub(r'"\nmsgstr ""\n"', "=", text)
text = re.sub(r'"\nmsgstr "', "=", text)
# various line breaks and escape codes
text = re.sub(r'"\n"', "", text)
text = re.sub(r'"\n', "\n", text)
text = re.sub(r'\\"', '"', text)
text = re.sub(r'\\n', '@n', text)
# remove header text
text = re.sub(r'=Project-Id-Version:.*\n', "", text)
# remove double-spaced lines
text = re.sub(r'\n\n', '\n', text)
return text
# Go through existing .po files and, if a .tr file for that language
# *doesn't* exist, convert it and create it.
# The .tr file that results will subsequently be reprocessed so
# any "no longer used" strings will be preserved.
# Note that "fuzzy" tags will be lost in this process.
def process_po_files(folder, modname):
for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
for name in files:
code_match = pattern_po_language_code.match(name)
if code_match == None:
continue
language_code = code_match.group(1)
tr_name = modname + "." + language_code + ".tr"
tr_file = os.path.join(root, tr_name)
if os.path.exists(tr_file):
if params["verbose"]:
print(f"{tr_name} already exists, ignoring {name}")
continue
fname = os.path.join(root, name)
with open(fname, "r", encoding='utf-8') as po_file:
if params["verbose"]:
print(f"Importing translations from {name}")
text = process_po_file(po_file.read())
with open(tr_file, "wt", encoding='utf-8') as tr_out:
tr_out.write(text)
# from https://stackoverflow.com/questions/600268/mkdir-p-functionality-in-python/600612#600612
# Creates a directory if it doesn't exist, silently does
# nothing if it already exists
def mkdir_p(path):
try:
os.makedirs(path)
except OSError as exc: # Python >2.5
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else: raise
# Converts the template dictionary to a text to be written as a file
# dKeyStrings is a dictionary of localized string to source file sets
# dOld is a dictionary of existing translations and comments from
# the previous version of this text
def strings_to_text(dkeyStrings, dOld, mod_name):
lOut = [f"# textdomain: {mod_name}\n"]
dGroupedBySource = {}
for key in dkeyStrings:
sourceList = list(dkeyStrings[key])
sourceList.sort()
sourceString = "\n".join(sourceList)
listForSource = dGroupedBySource.get(sourceString, [])
listForSource.append(key)
dGroupedBySource[sourceString] = listForSource
lSourceKeys = list(dGroupedBySource.keys())
lSourceKeys.sort()
for source in lSourceKeys:
localizedStrings = dGroupedBySource[source]
localizedStrings.sort()
lOut.append("")
lOut.append(source)
lOut.append("")
for localizedString in localizedStrings:
val = dOld.get(localizedString, {})
translation = val.get("translation", "")
comment = val.get("comment")
if len(localizedString) > doublespace_threshold and not lOut[-1] == "":
lOut.append("")
if comment != None:
lOut.append(comment)
lOut.append(f"{localizedString}={translation}")
if len(localizedString) > doublespace_threshold:
lOut.append("")
unusedExist = False
for key in dOld:
if key not in dkeyStrings:
val = dOld[key]
translation = val.get("translation")
comment = val.get("comment")
# only keep an unused translation if there was translated
# text or a comment associated with it
if translation != None and (translation != "" or comment):
if not unusedExist:
unusedExist = True
lOut.append("\n\n##### not used anymore #####\n")
if len(key) > doublespace_threshold and not lOut[-1] == "":
lOut.append("")
if comment != None:
lOut.append(comment)
lOut.append(f"{key}={translation}")
if len(key) > doublespace_threshold:
lOut.append("")
return "\n".join(lOut) + '\n'
# Writes a template.txt file
# dkeyStrings is the dictionary returned by generate_template
def write_template(templ_file, dkeyStrings, mod_name):
# read existing template file to preserve comments
existing_template = import_tr_file(templ_file)
text = strings_to_text(dkeyStrings, existing_template[0], mod_name)
mkdir_p(os.path.dirname(templ_file))
with open(templ_file, "wt", encoding='utf-8') as template_file:
template_file.write(text)
# Gets all translatable strings from a lua file
def read_lua_file_strings(lua_file):
lOut = []
with open(lua_file, encoding='utf-8') as text_file:
text = text_file.read()
#TODO remove comments here
text = re.sub(pattern_concat, "", text)
strings = []
for s in pattern_lua.findall(text):
strings.append(s[1])
for s in pattern_lua_bracketed.findall(text):
strings.append(s)
for s in strings:
s = re.sub(r'"\.\.\s+"', "", s)
s = re.sub("@[^@=0-9]", "@@", s)
s = s.replace('\\"', '"')
s = s.replace("\\'", "'")
s = s.replace("\n", "@n")
s = s.replace("\\n", "@n")
s = s.replace("=", "@=")
lOut.append(s)
return lOut
# Gets strings from an existing translation file
# returns both a dictionary of translations
# and the full original source text so that the new text
# can be compared to it for changes.
def import_tr_file(tr_file):
dOut = {}
text = None
if os.path.exists(tr_file):
with open(tr_file, "r", encoding='utf-8') as existing_file :
# save the full text to allow for comparison
# of the old version with the new output
text = existing_file.read()
existing_file.seek(0)
# a running record of the current comment block
# we're inside, to allow preceeding multi-line comments
# to be retained for a translation line
latest_comment_block = None
for line in existing_file.readlines():
line = line.rstrip('\n')
if line[:3] == "###":
# Reset comment block if we hit a header
latest_comment_block = None
continue
if line[:1] == "#":
# Save the comment we're inside
if not latest_comment_block:
latest_comment_block = line
else:
latest_comment_block = latest_comment_block + "\n" + line
continue
match = pattern_tr.match(line)
if match:
# this line is a translated line
outval = {}
outval["translation"] = match.group(2)
if latest_comment_block:
# if there was a comment, record that.
outval["comment"] = latest_comment_block
latest_comment_block = None
dOut[match.group(1)] = outval
return (dOut, text)
# Walks all lua files in the mod folder, collects translatable strings,
# and writes it to a template.txt file
# Returns a dictionary of localized strings to source file sets
# that can be used with the strings_to_text function.
def generate_template(folder, mod_name):
dOut = {}
for root, dirs, files in os.walk(folder):
for name in files:
if fnmatch.fnmatch(name, "*.lua"):
fname = os.path.join(root, name)
found = read_lua_file_strings(fname)
if params["verbose"]:
print(f"{fname}: {str(len(found))} translatable strings")
for s in found:
sources = dOut.get(s, set())
sources.add(f"### {os.path.basename(fname)} ###")
dOut[s] = sources
if len(dOut) == 0:
return None
templ_file = os.path.join(folder, "locale/template.txt")
write_template(templ_file, dOut, mod_name)
return dOut
# Updates an existing .tr file, copying the old one to a ".old" file
# if any changes have happened
# dNew is the data used to generate the template, it has all the
# currently-existing localized strings
def update_tr_file(dNew, mod_name, tr_file):
if params["verbose"]:
print(f"updating {tr_file}")
tr_import = import_tr_file(tr_file)
dOld = tr_import[0]
textOld = tr_import[1]
textNew = strings_to_text(dNew, dOld, mod_name)
if textOld and textOld != textNew:
print(f"{tr_file} has changed.")
shutil.copyfile(tr_file, f"{tr_file}.old")
with open(tr_file, "w", encoding='utf-8') as new_tr_file:
new_tr_file.write(textNew)
# Updates translation files for the mod in the given folder
def update_mod(folder):
modname = get_modname(folder)
if modname is not None:
process_po_files(folder, modname)
print(f"Updating translations for {modname}")
data = generate_template(folder, modname)
if data == None:
print(f"No translatable strings found in {modname}")
else:
for tr_file in get_existing_tr_files(folder):
update_tr_file(data, modname, os.path.join(folder, "locale/", tr_file))
else:
print("Unable to find modname in folder " + folder)
# Determines if the folder being pointed to is a mod or a mod pack
# and then runs update_mod accordingly
def update_folder(folder):
is_modpack = os.path.exists(os.path.join(folder, "modpack.txt")) or os.path.exists(os.path.join(folder, "modpack.conf"))
if is_modpack:
subfolders = [f.path for f in os.scandir(folder) if f.is_dir()]
for subfolder in subfolders:
update_mod(subfolder + "/")
else:
update_mod(folder)
print("Done.")
def run_all_subfolders(folder):
for modfolder in [f.path for f in os.scandir(folder) if f.is_dir()]:
update_folder(modfolder + "/")
main()

View File

@ -1,6 +1,4 @@
-- internationalization boilerplate
local MP = minetest.get_modpath(minetest.get_current_modname())
local S, NS = dofile(MP.."/intllib.lua")
local S = minetest.get_translator("crafting_bench")
minetest.register_alias("castle:workbench", "crafting_bench:workbench")
@ -17,7 +15,7 @@ if crafting_rate == nil then crafting_rate = 5 end
minetest.register_node("crafting_bench:workbench",{
description = S("Workbench"),
_doc_items_longdesc = string.format(S("A workbench that does work for you. Set a crafting recipe and provide raw materials and items will magically craft themselves once every %i seconds."), crafting_rate),
_doc_items_longdesc = S("A workbench that does work for you. Set a crafting recipe and provide raw materials and items will magically craft themselves once every @1 seconds.", crafting_rate),
_doc_items_usagehelp = usage_help,
tiles = {
"crafting_bench_workbench_top.png",
@ -58,13 +56,13 @@ minetest.register_node("crafting_bench:workbench",{
return inv:is_empty("main")
end,
on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
minetest.log("action", S("@1 moves stuff in workbench at @2", player:get_player_name(), minetest.pos_to_string(pos)))
minetest.log("action", player:get_player_name().." moves stuff in workbench at "..minetest.pos_to_string(pos))
end,
on_metadata_inventory_put = function(pos, listname, index, stack, player)
minetest.log("action", S("@1 moves stuff to workbench at @2", player:get_player_name(), minetest.pos_to_string(pos)))
minetest.log("action", player:get_player_name().." moves stuff to workbench at "..minetest.pos_to_string(pos))
end,
on_metadata_inventory_take = function(pos, listname, index, stack, player)
minetest.log("action", S("@1 takes stuff from workbench at @2", player:get_player_name(), minetest.pos_to_string(pos)))
minetest.log("action", player:get_player_name().." takes stuff from workbench at "..minetest.pos_to_string(pos))
end,
})
local get_recipe = function ( inv )

View File

@ -1,45 +0,0 @@
-- Fallback functions for when `intllib` is not installed.
-- Code released under Unlicense <http://unlicense.org>.
-- Get the latest version of this file at:
-- https://raw.githubusercontent.com/minetest-mods/intllib/master/lib/intllib.lua
local function format(str, ...)
local args = { ... }
local function repl(escape, open, num, close)
if escape == "" then
local replacement = tostring(args[tonumber(num)])
if open == "" then
replacement = replacement..close
end
return replacement
else
return "@"..open..num..close
end
end
return (str:gsub("(@?)@(%(?)(%d+)(%)?)", repl))
end
local gettext, ngettext
if minetest.get_modpath("intllib") then
if intllib.make_gettext_pair then
-- New method using gettext.
gettext, ngettext = intllib.make_gettext_pair()
else
-- Old method using text files.
gettext = intllib.Getter()
end
end
-- Fill in missing functions.
gettext = gettext or function(msgid, ...)
return format(msgid, ...)
end
ngettext = ngettext or function(msgid, msgid_plural, n, ...)
return format(n==1 and msgid or msgid_plural, ...)
end
return gettext, ngettext

View File

@ -0,0 +1,16 @@
# textdomain: crafting_bench
### init.lua ###
A workbench that does work for you. Set a crafting recipe and provide raw materials and items will magically craft themselves once every @1 seconds.=Un établi qui travaille pour vous. Définissez une recette de fabrication et fournissez les matériaux, et le résultat se fabriquera par magie toutes les @1 secondes.
Craft Output=Produits finis
Recipe to Use=Recette à utiliser
Source Material=Matériaux à utiliser
The inventory on the left is for raw materials, the inventory on the right holds finished products. The crafting grid in the center defines what recipe this workbench will make use of; place raw materials into it in the crafting pattern corresponding to what you want to build.=Linventaire de gauche est pour les matériaux à utiliser, linventaire de droite contient les produits finis. La grille de fabrication au centre définit la recette que létabli utilisera ; placez des matériaux dans la grille en suivant le pattern qui correspond à ce que vous voulez fabriquer.
This workbench is compatible with hoppers. Hoppers will insert into the raw material inventory and remove items from the finished goods inventory.=Cet établi est compatible avec les entonnoirs. Les entonoirs peuvent insérer des matériaux à utiliser et retirer les produits finis depuis les bons inventaires.
Workbench=Établi

View File

@ -1,67 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-02-28 21:08-0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: init.lua:7
msgid ""
"The inventory on the left is for raw materials, the inventory on the right "
"holds finished products. The crafting grid in the center defines what recipe "
"this workbench will make use of; place raw materials into it in the crafting "
"pattern corresponding to what you want to build."
msgstr ""
#: init.lua:10
msgid ""
"This workbench is compatible with hoppers. Hoppers will insert into the raw "
"material inventory and remove items from the finished goods inventory."
msgstr ""
#: init.lua:19 init.lua:49
msgid "Workbench"
msgstr ""
#: init.lua:20
#, lua-format
msgid ""
"A workbench that does work for you. Set a crafting recipe and provide raw "
"materials and items will magically craft themselves once every %i seconds."
msgstr ""
#: init.lua:42
msgid "Source Material"
msgstr ""
#: init.lua:44
msgid "Recipe to Use"
msgstr ""
#: init.lua:46
msgid "Craft Output"
msgstr ""
#: init.lua:61
msgid "@1 moves stuff in workbench at @2"
msgstr ""
#: init.lua:64
msgid "@1 moves stuff to workbench at @2"
msgstr ""
#: init.lua:67
msgid "@1 takes stuff from workbench at @2"
msgstr ""

View File

@ -0,0 +1,16 @@
# textdomain: crafting_bench
### init.lua ###
A workbench that does work for you. Set a crafting recipe and provide raw materials and items will magically craft themselves once every @1 seconds.=
Craft Output=
Recipe to Use=
Source Material=
The inventory on the left is for raw materials, the inventory on the right holds finished products. The crafting grid in the center defines what recipe this workbench will make use of; place raw materials into it in the crafting pattern corresponding to what you want to build.=
This workbench is compatible with hoppers. Hoppers will insert into the raw material inventory and remove items from the finished goods inventory.=
Workbench=

View File

@ -1 +1,4 @@
name = crafting_bench
description = An auto-crafting bench
depends = default
optional_depends = hopper, doc

View File

@ -1,8 +1,6 @@
barter = {}
-- internationalization boilerplate
local MP = minetest.get_modpath(minetest.get_current_modname())
local S, NS = dofile(MP.."/intllib.lua")
local S = minetest.get_translator("currency")
barter.chest = {}
barter.chest.expire_after = tonumber(minetest.settings:get('barter.chest.expireafter')) or 15 * 60
@ -12,20 +10,20 @@ barter.chest.formspec = {
"list[current_name;pl2;5,0;3,4;]"..
"list[current_player;main;0,5;8,4;]",
pl1 = {
start = "button[3,1;1,1;pl1_start;" .. S("Start") .. "]",
player = function(name) return "label[3,0;"..name.."]" end,
accept1 = "button[3,1;1,1;pl1_accept1;" .. S("Confirm") .. "]"..
"button[3,2;1,1;pl1_cancel;" .. S("Cancel") .. "]",
accept2 = "button[3,1;1,1;pl1_accept2;" .. S("Exchange") .. "]"..
"button[3,2;1,1;pl1_cancel;" .. S("Cancel") .. "]",
start = "button[0,4;3,1;pl1_start;" .. S("Start") .. "]",
player = function(name) return "label[0,4;"..name.."]" end,
accept1 = "button[2.9,1;1.2,1;pl1_accept1;" .. S("Confirm") .. "]"..
"button[2.9,2;1.2,1;pl1_cancel;" .. S("Cancel") .. "]",
accept2 = "button[2.9,1;1.2,1;pl1_accept2;" .. S("Exchange") .. "]"..
"button[2.9,2;1.2,1;pl1_cancel;" .. S("Cancel") .. "]",
},
pl2 = {
start = "button[4,1;1,1;pl2_start;" .. S("Start") .. "]",
player = function(name) return "label[4,0;"..name.."]" end,
accept1 = "button[4,1;1,1;pl2_accept1;" .. S("Confirm") .. "]"..
"button[4,2;1,1;pl2_cancel;" .. S("Cancel") .. "]",
accept2 = "button[4,1;1,1;pl2_accept2;" .. S("Exchange") .. "]"..
"button[4,2;1,1;pl2_cancel;" .. S("Cancel") .. "]",
start = "button[5,4;3,1;pl2_start;" .. S("Start") .. "]",
player = function(name) return "label[5,4;"..name.."]" end,
accept1 = "button[3.9,1;1.2,1;pl2_accept1;" .. S("Confirm") .. "]"..
"button[3.9,2;1.2,1;pl2_cancel;" .. S("Cancel") .. "]",
accept2 = "button[3.9,1;1.2,1;pl2_accept2;" .. S("Exchange") .. "]"..
"button[3.9,2;1.2,1;pl2_cancel;" .. S("Cancel") .. "]",
},
}

View File

@ -1,58 +1,56 @@
-- internationalization boilerplate
local MP = minetest.get_modpath(minetest.get_current_modname())
local S, NS = dofile(MP.."/intllib.lua")
local S = minetest.get_translator("currency")
minetest.register_craftitem("currency:minegeld_cent_5", {
description = S("5 Minegeld cent coin"),
description = S("@1 Minegeld cent coin", "5"),
inventory_image = "minegeld_cent_5.png",
stack_max = 1000,
groups = {minegeld = 1}
})
minetest.register_craftitem("currency:minegeld_cent_10", {
description = S("10 Minegeld cent coin"),
description = S("@1 Minegeld cent coin", "10"),
inventory_image = "minegeld_cent_10.png",
stack_max = 1000,
groups = {minegeld = 1}
})
minetest.register_craftitem("currency:minegeld_cent_25", {
description = S("25 Minegeld cent coin"),
description = S("@1 Minegeld cent coin", "25"),
inventory_image = "minegeld_cent_25.png",
stack_max = 1000,
groups = {minegeld = 1}
})
minetest.register_craftitem("currency:minegeld", {
description = S("1 Minegeld Note"),
description = S("@1 Minegeld Note", "1"),
inventory_image = "minegeld.png",
stack_max = 65535,
groups = {minegeld = 1}
})
minetest.register_craftitem("currency:minegeld_5", {
description = S("5 Minegeld Note"),
description = S("@1 Minegeld Note", "5"),
inventory_image = "minegeld_5.png",
stack_max = 65535,
groups = {minegeld = 1}
})
minetest.register_craftitem("currency:minegeld_10", {
description = S("10 Minegeld Note"),
description = S("@1 Minegeld Note", "10"),
inventory_image = "minegeld_10.png",
stack_max = 65535,
groups = {minegeld = 1}
})
minetest.register_craftitem("currency:minegeld_50", {
description = S("50 Minegeld Note"),
description = S("@1 Minegeld Note", "50"),
inventory_image = "minegeld_50.png",
stack_max = 65535,
groups = {minegeld = 1}
})
minetest.register_craftitem("currency:minegeld_100", {
description = S("100 Minegeld Note"),
description = S("@1 Minegeld Note", "100"),
inventory_image = "minegeld_100.png",
stack_max = 65535,
groups = {minegeld = 1}

View File

@ -1,4 +0,0 @@
default
intllib?
loot?
pipeworks?

View File

@ -1 +0,0 @@
Provides shops, barter tables, safes, and multiple denominations of currency, called "Minegeld".

View File

@ -1,9 +1,5 @@
local players_income = {}
-- internationalization boilerplate
local MP = minetest.get_modpath(minetest.get_current_modname())
local S, NS = dofile(MP.."/intllib.lua")
local income_enabled = minetest.settings:get_bool("currency.income_enabled", true)
local income_item = minetest.settings:get("currency.income_item") or "currency:minegeld_10"
local income_count = tonumber(minetest.settings:get("currency.income_count")) or 1
@ -18,7 +14,7 @@ if income_enabled then
for _, player in ipairs(minetest.get_connected_players()) do
local name = player:get_player_name()
players_income[name] = income_count
minetest.log("info", "[Currency] "..S("basic income for @1", name))
minetest.log("info", "[Currency] basic income for "..name)
end
end
end)
@ -32,7 +28,7 @@ if income_enabled then
local inv = player:get_inventory()
inv:add_item("main", {name=income_item, count=income_count})
players_income[name] = nil
minetest.log("info", "[Currency] "..S("added basic income for @1 to inventory", name))
minetest.log("info", "[Currency] added basic income for "..name.." to inventory")
end
end

View File

@ -1,24 +1,21 @@
local modpath = minetest.get_modpath("currency")
-- internationalization boilerplate
local S, NS = dofile(modpath.."/intllib.lua")
minetest.log("info", S("Currency mod loading..."))
minetest.log("info", "Currency mod loading...")
dofile(modpath.."/craftitems.lua")
minetest.log("info", "[Currency] "..S("Craft_items Loaded!"))
minetest.log("info", "[Currency] Craft_items Loaded!")
dofile(modpath.."/shop.lua")
minetest.log("info", "[Currency] "..S("Shop Loaded!"))
minetest.log("info", "[Currency] Shop Loaded!")
dofile(modpath.."/barter.lua")
minetest.log("info", "[Currency] "..S("Barter Loaded!"))
minetest.log("info", "[Currency] Barter Loaded!")
dofile(modpath.."/safe.lua")
minetest.log("info", "[Currency] "..S("Safe Loaded!"))
minetest.log("info", "[Currency] Safe Loaded!")
dofile(modpath.."/crafting.lua")
minetest.log("info", "[Currency] "..S("Crafting Loaded!"))
minetest.log("info", "[Currency] Crafting Loaded!")
if minetest.settings:get_bool("creative_mode") then
minetest.log("info", "[Currency] "..S("Creative mode in use, skipping basic income."))
minetest.log("info", "[Currency] Creative mode in use, skipping basic income.")
else
dofile(modpath.."/income.lua")
minetest.log("info", "[Currency] "..S("Income Loaded!"))
minetest.log("info", "[Currency] Income Loaded!")
end

View File

@ -1,45 +0,0 @@
-- Fallback functions for when `intllib` is not installed.
-- Code released under Unlicense <http://unlicense.org>.
-- Get the latest version of this file at:
-- https://raw.githubusercontent.com/minetest-mods/intllib/master/lib/intllib.lua
local function format(str, ...)
local args = { ... }
local function repl(escape, open, num, close)
if escape == "" then
local replacement = tostring(args[tonumber(num)])
if open == "" then
replacement = replacement..close
end
return replacement
else
return "@"..open..num..close
end
end
return (str:gsub("(@?)@(%(?)(%d+)(%)?)", repl))
end
local gettext, ngettext
if minetest.get_modpath("intllib") then
if intllib.make_gettext_pair then
-- New method using gettext.
gettext, ngettext = intllib.make_gettext_pair()
else
-- Old method using text files.
gettext = intllib.Getter()
end
end
-- Fill in missing functions.
gettext = gettext or function(msgid, ...)
return format(msgid, ...)
end
ngettext = ngettext or function(msgid, msgid_plural, n, ...)
return format(n==1 and msgid or msgid_plural, ...)
end
return gettext, ngettext

View File

@ -0,0 +1,46 @@
# textdomain: currency
# A.C.M. <undertakers_help@yahoo.com>, 2018
### barter.lua ###
Barter Table=Tausch Tisch
Cancel=Abbruch
Confirm=Bestätigen
Start=Start
### shop.lua ###
Exchange=Tausch
### craftitems.lua ###
@1 Minegeld Note=@1 Minegeld Banknote
@1 Minegeld cent coin=
Bundle of random Minegeld notes=Bündel von verschiedenen Minegeld Banknoten
### safe.lua ###
Safe=Safe
Safe (owned by @1)=Safe (gehört @1)
### shop.lua ###
Customer gets:=Kunde bekommt:
Customer gives (pay here!)=Kunde gibt (bezahl hier!)
Customers gave:=Kunde gab:
Exchange can not be done, check if you put all items!=Tausch kann nicht abgeschlossen werden, prüfe nochmal alles was du hinein gelegt hast!
Exchange can not be done, contact the shop owner.=Tausch kann nicht abgeschlossen werden, benachrichtige den Geschäftsinhaber.
Exchange shop (owned by @1)=Tauschgeschäft (gehört @1)
Exchanged!=Getauscht!
In exchange, you give:=Im Tausch, du gibst:
Owner gives:=Inhaber gibt:
Owner wants:=Inhaber will:
Owner, Use (E)+Place (right mouse button) for customer interface=Inhaber, Benutze (E)+Platziere (rechte Maustaste) für Kunde ...
Shop=Geschäft
This is your own shop, you can't exchange to yourself!=Das ist dein eigenes Geschäft, du kannst nicht mit dir selbst tauschen!
You want:=Du willst:
Your stock:=Dein Lager:

View File

@ -0,0 +1,47 @@
# textdomain: currency
# Papou30, 2018.
# Louis Royer, 2020.
### barter.lua ###
Barter Table=Table de troc
Cancel=Annuler
Confirm=Valider
Start=Commencer
### shop.lua ###
Exchange=Troquer
### craftitems.lua ###
@1 Minegeld Note=@1 MineGeld
@1 Minegeld cent coin=Pièce de @1 centimes de MineGeld
Bundle of random Minegeld notes=Liasse de Minegeld de diverses valeurs
### safe.lua ###
Safe=Coffre-fort
Safe (owned by @1)=Coffre-fort (appartenant à @1)
### shop.lua ###
Customer gets:=Client obtient:
Customer gives (pay here!)=Client donne (payer ici!)
Customers gave:=Le client a donné:
Exchange can not be done, check if you put all items!=L'echange ne peut pas être fait, vérifiez que vous avez placé tous les articles!
Exchange can not be done, contact the shop owner.=L'échange ne peut être fait, contactez le propriétaire du magasin.
Exchange shop (owned by @1)=Boutique d'échange (appartenant à @1)
Exchanged!=Échangé!
In exchange, you give:=En échange, vous donnez :
Owner gives:=Le propriétaire donne :
Owner wants:=Le propriétaire veut :
Owner, Use (E)+Place (right mouse button) for customer interface=Propriétaire, utilisez Spécial+clic droit pour l'interface client
Shop=Boutique
This is your own shop, you can't exchange to yourself!=C'est votre propre boutique, vous ne pouvez pas échanger avec vous-même!
You want:=Vous voulez :
Your stock:=Votre stock :

View File

@ -0,0 +1,46 @@
# textdomain: currency
### barter.lua ###
Barter Table=Meja Barter
Cancel=Batal
Confirm=Sah
Start=Mula
### shop.lua ###
Exchange=Tukar
### craftitems.lua ###
@1 Minegeld Note=Wang @1 MineGeld
@1 Minegeld cent coin=
Bundle of random Minegeld notes=Seberkas wang Minegeld rawak
### safe.lua ###
Safe=Peti besi
Safe (owned by @1)=Peti besi (pemilik: @1)
### shop.lua ###
Customer gets:=Pelanggan dapat:
Customer gives (pay here!)=Pelanggan beri (bayar sini!)
Customers gave:=Pelanggan berikan:
Exchange can not be done, check if you put all items!=Tak boleh jual, pastikan anda ada apa yang penjual mahukan!
Exchange can not be done, contact the shop owner.=Tak boleh jual, hubungi penjual.
Exchange shop (owned by @1)=Kedai (pemilik: @1)
Exchanged!=Jual!
In exchange, you give:=Anda berikan:
Owner gives:=Penjual berikan:
Owner wants:=Penjual inginkan:
Owner, Use (E)+Place (right mouse button) for customer interface=Penjual, Gunakan (E) + Letak (butang tetikus kanan) untuk antaramuka pelanggan
Shop=Kedai
This is your own shop, you can't exchange to yourself!=Ini kedai anda, anda tidak boleh menjual kepada diri sendiri!
You want:=Anda mahukan:
Your stock:=Stok anda:

View File

@ -0,0 +1,46 @@
# textdomain: currency
### barter.lua ###
Barter Table=Таблица бартера
Cancel=Отмена
Confirm=Подтверждение
Start=Старт
### shop.lua ###
Exchange=Обмен
### craftitems.lua ###
@1 Minegeld Note=Банкнота в @1 MineGeld
@1 Minegeld cent coin=
Bundle of random Minegeld notes=Пачка случайных банкнот Minegeld
### safe.lua ###
Safe=Сейф
Safe (owned by @1)=Сейф (принадлежит @1)
### shop.lua ###
Customer gets:=Покупатель получает:
Customer gives (pay here!)=Покупатель отдаёт (здесь оплата!)
Customers gave:=Покупатель отдал:
Exchange can not be done, check if you put all items!=Обмен невозможен, проверьте, что вы положили все предметы!
Exchange can not be done, contact the shop owner.=Обмен невозможен, свяжитесь с владельцем магазина.
Exchange shop (owned by @1)=Пункт обмена (принадлежит @1)
Exchanged!=Обмен произведён!
In exchange, you give:=Взамен вы даёте:
Owner gives:=Владелец отдаёт:
Owner wants:=Владелец хочет:
Owner, Use (E)+Place (right mouse button) for customer interface=Владелец, используйте (E)+Разместить (правая кнопка мыши) для открытия интерфейса покупателя
Shop=Магазин
This is your own shop, you can't exchange to yourself!=Это ваш собственный магазин, вы не можете обмениваться с самим собой!
You want:=Вы хотите:
Your stock:=Ваш запас:

View File

@ -1,183 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-05-24 22:01+0200\n"
"PO-Revision-Date: 2018-05-24 22:48+0200\n"
"Last-Translator: A.C.M. <undertakers_help@yahoo.com>\n"
"Language-Team: \n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.0.7\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: barter.lua
msgid "Start"
msgstr "Start"
#: barter.lua
msgid "Confirm"
msgstr "Bestätigen"
#: barter.lua
msgid "Cancel"
msgstr "Abbruch"
#: barter.lua shop.lua
msgid "Exchange"
msgstr "Tausch"
#: barter.lua
msgid "Barter Table"
msgstr "Tausch Tisch"
#: craftitems.lua
msgid "1 MineGeld Note"
msgstr "1 Minegeld Banknote"
#: craftitems.lua
msgid "5 MineGeld Note"
msgstr "5 Minegeld Banknote"
#: craftitems.lua
msgid "10 MineGeld Note"
msgstr "10 Minegeld Banknote"
#: craftitems.lua
msgid "50 MineGeld Note"
msgstr "50 Minegeld Banknote"
#: craftitems.lua
msgid "Bundle of random Minegeld notes"
msgstr "Bündel von verschiedenen Minegeld Banknoten"
#: income.lua
msgid "basic income for @1"
msgstr "Grundeinkommen für @1"
#: income.lua
msgid "added basic income for @1 to inventory"
msgstr "Grundeinkommen für @1 ins Inventar gelegt"
#: init.lua
msgid "Currency mod loading..."
msgstr "Currency mod lädt..."
#: init.lua
msgid "Craft_items Loaded!"
msgstr "Craft_items geladen!"
#: init.lua
msgid "Shop Loaded!"
msgstr "Geschäft geladen!"
#: init.lua
msgid "Barter Loaded!"
msgstr "Tauschtisch geladen!"
#: init.lua
msgid "Safe Loaded!"
msgstr "Safe geladen!"
#: init.lua
msgid "Crafting Loaded!"
msgstr "Crafting geladen!"
#: init.lua
msgid "Creative mode in use, skipping basic income."
msgstr "Kreativmodus eingeschalten, überspringe Grundeinkommen."
#: init.lua
msgid "Income Loaded!"
msgstr "Income geladen!"
#: safe.lua
msgid "Safe"
msgstr "Safe"
#: safe.lua
msgid "Safe (owned by @1)"
msgstr "Safe (gehört @1)"
#: safe.lua
msgid "@1 tried to access a safe belonging to @2 at @3"
msgstr "@1 versuchte den Zugriff auf Safe von @2 bei @3"
#: safe.lua
msgid "@1 moves stuff in safe at @2"
msgstr "@1 verschiebt Gegenstände in den Safe bei @2"
#: safe.lua
msgid "@1 moves stuff to safe at @2"
msgstr "@1 verschiebt Gegenstände nach Safe bei @2"
#: safe.lua
msgid "@1 takes stuff from safe at @2"
msgstr "@1 nimmt Gegenstände aus Safe bei @2"
#: shop.lua
msgid "Customer gives (pay here!)"
msgstr "Kunde gibt (bezahl hier!)"
#: shop.lua
msgid "Customer gets:"
msgstr "Kunde bekommt:"
#: shop.lua
msgid "Owner wants:"
msgstr "Inhaber will:"
#: shop.lua
msgid "Owner gives:"
msgstr "Inhaber gibt:"
#: shop.lua
msgid "Customers gave:"
msgstr "Kunde gab:"
#: shop.lua
msgid "Your stock:"
msgstr "Dein Lager:"
#: shop.lua
msgid "You want:"
msgstr "Du willst:"
#: shop.lua
msgid "In exchange, you give:"
msgstr "Im Tausch, du gibst:"
#: shop.lua
msgid "Owner, Use (E)+Place (right mouse button) for customer interface"
msgstr "Inhaber, Benutze (E)+Platziere (rechte Maustaste) für Kunde ..."
#: shop.lua
msgid "Shop"
msgstr "Geschäft"
#: shop.lua
msgid "Exchange shop (owned by @1)"
msgstr "Tauschgeschäft (gehört @1)"
#: shop.lua
msgid "This is your own shop, you can't exchange to yourself!"
msgstr "Das ist dein eigenes Geschäft, du kannst nicht mit dir selbst tauschen!"
#: shop.lua
msgid "Exchanged!"
msgstr "Getauscht!"
#: shop.lua
msgid "Exchange can not be done, contact the shop owner."
msgstr "Tausch kann nicht abgeschlossen werden, benachrichtige den Geschäftsinhaber."
#: shop.lua
msgid "Exchange can not be done, check if you put all items!"
msgstr "Tausch kann nicht abgeschlossen werden, prüfe nochmal alles was du hinein gelegt hast!"

View File

@ -1,187 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-05-24 23:06+0200\n"
"PO-Revision-Date: 2018-05-12 14:55+0200\n"
"Last-Translator: Papou30\n"
"Language-Team: \n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.0.7\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: barter.lua
msgid "Start"
msgstr "Début"
#: barter.lua
msgid "Confirm"
msgstr "Confirmer"
#: barter.lua
msgid "Cancel"
msgstr "Annuler"
#: barter.lua shop.lua
msgid "Exchange"
msgstr "Échange"
#: barter.lua
msgid "Barter Table"
msgstr "Table de troc"
#: craftitems.lua
msgid "1 MineGeld Note"
msgstr "1 MineGeld"
#: craftitems.lua
msgid "5 MineGeld Note"
msgstr "5 MineGeld"
#: craftitems.lua
msgid "10 MineGeld Note"
msgstr "10 MineGeld"
#: craftitems.lua
#, fuzzy
msgid "50 MineGeld Note"
msgstr "5 MineGeld"
#: craftitems.lua
msgid "Bundle of random Minegeld notes"
msgstr "Liasse de Minegeld aléatoire"
#: income.lua
msgid "basic income for @1"
msgstr "revenu de base pour @1"
#: income.lua
msgid "added basic income for @1 to inventory"
msgstr "revenu de base ajouté pour @1 à l'inventaire"
#: init.lua
msgid "Currency mod loading..."
msgstr "Chargement du mod Currency..."
#: init.lua
msgid "Craft_items Loaded!"
msgstr "Fabrication d'objets (Craft_items) chargé!"
#: init.lua
msgid "Shop Loaded!"
msgstr "Boutique (Shop) chargée!"
#: init.lua
msgid "Barter Loaded!"
msgstr "Troc (Barter) chargé!"
#: init.lua
msgid "Safe Loaded!"
msgstr "Coffre-fort (Safe) chargé!"
#: init.lua
msgid "Crafting Loaded!"
msgstr "Artisanat (Crafting) chargé!"
#: init.lua
msgid "Creative mode in use, skipping basic income."
msgstr "Mod créatif en cours d'utilisation, ignoré le revenu de base."
#: init.lua
msgid "Income Loaded!"
msgstr "Revenu de base (Income) chargé!"
#: safe.lua
msgid "Safe"
msgstr "Coffre-fort"
#: safe.lua
msgid "Safe (owned by @1)"
msgstr "Coffre-fort (appartenant à @1)"
#: safe.lua
msgid "@1 tried to access a safe belonging to @2 at @3"
msgstr "@1 a tenté d'accéder à un coffre-fort appartenant à @2 à @3"
#: safe.lua
msgid "@1 moves stuff in safe at @2"
msgstr "@1 déplace le matériel dans coffre-fort à @2"
#: safe.lua
msgid "@1 moves stuff to safe at @2"
msgstr "@1 déplace le matériel vers coffre-fort à @2"
#: safe.lua
msgid "@1 takes stuff from safe at @2"
msgstr "@1 prend le matériel dans le coffre-fort à @2"
#: shop.lua
msgid "Customer gives (pay here!)"
msgstr "Client donne (payer ici!)"
#: shop.lua
msgid "Customer gets:"
msgstr "Client obtient:"
#: shop.lua
msgid "Owner wants:"
msgstr "Le propriétaire veut:"
#: shop.lua
msgid "Owner gives:"
msgstr "Propriétaire donne:"
#: shop.lua
msgid "Customers gave:"
msgstr "Le client a donné:"
#: shop.lua
msgid "Your stock:"
msgstr "Votre stock:"
#: shop.lua
msgid "You want:"
msgstr "Vous voulez:"
#: shop.lua
msgid "In exchange, you give:"
msgstr "En échange, vous donnez:"
#: shop.lua
msgid "Owner, Use (E)+Place (right mouse button) for customer interface"
msgstr ""
"Propriétaire, utilisez (E) + place (bouton droit de la souris) pour "
"l'interface client"
#: shop.lua
msgid "Shop"
msgstr "Boutique"
#: shop.lua
msgid "Exchange shop (owned by @1)"
msgstr "Boutique d'échange (détenue par @1)"
#: shop.lua
msgid "This is your own shop, you can't exchange to yourself!"
msgstr "C'est ta propre boutique, tu ne peux pas échanger avec toi-même!"
#: shop.lua
msgid "Exchanged!"
msgstr "Échangé!"
#: shop.lua
msgid "Exchange can not be done, contact the shop owner."
msgstr "L'échange ne peut être fait, contactez le propriétaire du magasin."
#: shop.lua
msgid "Exchange can not be done, check if you put all items!"
msgstr ""
"L'echange ne peut pas etre fait, verifiez si vous mettez tous les articles!"

View File

@ -1,186 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-05-24 23:06+0200\n"
"PO-Revision-Date: 2017-10-17 02:03+0800\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: ms\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.0.4\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: barter.lua
msgid "Start"
msgstr "Mula"
#: barter.lua
msgid "Confirm"
msgstr "Sah"
#: barter.lua
msgid "Cancel"
msgstr "Batal"
#: barter.lua shop.lua
msgid "Exchange"
msgstr "Tukar"
#: barter.lua
msgid "Barter Table"
msgstr "Meja Barter"
#: craftitems.lua
msgid "1 MineGeld Note"
msgstr "Wang 1 MineGeld"
#: craftitems.lua
msgid "5 MineGeld Note"
msgstr "Wang 5 MineGeld"
#: craftitems.lua
msgid "10 MineGeld Note"
msgstr "Wang 10 MineGeld"
#: craftitems.lua
#, fuzzy
msgid "50 MineGeld Note"
msgstr "Wang 5 MineGeld"
#: craftitems.lua
msgid "Bundle of random Minegeld notes"
msgstr "Seberkas wang Minegeld rawak"
#: income.lua
msgid "basic income for @1"
msgstr "pendapatan asas untuk @1"
#: income.lua
msgid "added basic income for @1 to inventory"
msgstr "pendapatan asas untuk @1 ditambah masuk ke dalam inventori"
#: init.lua
msgid "Currency mod loading..."
msgstr "Memuatkan mods Currency..."
#: init.lua
msgid "Craft_items Loaded!"
msgstr "Item pertukangan dimuatkan!"
#: init.lua
msgid "Shop Loaded!"
msgstr "Kedai dimuatkan!"
#: init.lua
msgid "Barter Loaded!"
msgstr "Meja barter dimuatkan!"
#: init.lua
msgid "Safe Loaded!"
msgstr "Peti besi dimuatkan!"
#: init.lua
msgid "Crafting Loaded!"
msgstr "Pertukangan dimuatkan!"
#: init.lua
msgid "Creative mode in use, skipping basic income."
msgstr "Dalam mod kreatif, pendapatan asas diabaikan."
#: init.lua
msgid "Income Loaded!"
msgstr "Pendapatan dimuatkan!"
#: safe.lua
msgid "Safe"
msgstr "Peti besi"
#: safe.lua
msgid "Safe (owned by @1)"
msgstr "Peti besi (pemilik: @1)"
#: safe.lua
msgid "@1 tried to access a safe belonging to @2 at @3"
msgstr "@1 cuba untuk mengakses peti besi milik @2 di @3"
#: safe.lua
msgid "@1 moves stuff in safe at @2"
msgstr "@1 pindahkan barang dalam peti besi di @2"
#: safe.lua
msgid "@1 moves stuff to safe at @2"
msgstr "@1 masukkan barang dalam peti besi di @2"
#: safe.lua
msgid "@1 takes stuff from safe at @2"
msgstr "@1 keluarkan barang dari peti besi di @2"
#: shop.lua
msgid "Customer gives (pay here!)"
msgstr "Pelanggan beri (bayar sini!)"
#: shop.lua
msgid "Customer gets:"
msgstr "Pelanggan dapat:"
#: shop.lua
msgid "Owner wants:"
msgstr "Penjual inginkan:"
#: shop.lua
msgid "Owner gives:"
msgstr "Penjual berikan:"
#: shop.lua
msgid "Customers gave:"
msgstr "Pelanggan berikan:"
#: shop.lua
msgid "Your stock:"
msgstr "Stok anda:"
#: shop.lua
msgid "You want:"
msgstr "Anda mahukan:"
#: shop.lua
msgid "In exchange, you give:"
msgstr "Anda berikan:"
#: shop.lua
msgid "Owner, Use (E)+Place (right mouse button) for customer interface"
msgstr ""
"Penjual, Gunakan (E) + Letak (butang tetikus kanan) untuk antaramuka "
"pelanggan"
#: shop.lua
msgid "Shop"
msgstr "Kedai"
#: shop.lua
msgid "Exchange shop (owned by @1)"
msgstr "Kedai (pemilik: @1)"
#: shop.lua
msgid "This is your own shop, you can't exchange to yourself!"
msgstr "Ini kedai anda, anda tidak boleh menjual kepada diri sendiri!"
#: shop.lua
msgid "Exchanged!"
msgstr "Jual!"
#: shop.lua
msgid "Exchange can not be done, contact the shop owner."
msgstr "Tak boleh jual, hubungi penjual."
#: shop.lua
msgid "Exchange can not be done, check if you put all items!"
msgstr "Tak boleh jual, pastikan anda ada apa yang penjual mahukan!"

View File

@ -1,187 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-05-24 23:06+0200\n"
"PO-Revision-Date: 2017-08-11 19:03+0300\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.0.3\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
#: barter.lua
msgid "Start"
msgstr "Старт"
#: barter.lua
msgid "Confirm"
msgstr "Подтверждение"
#: barter.lua
msgid "Cancel"
msgstr "Отмена"
#: barter.lua shop.lua
msgid "Exchange"
msgstr "Обмен"
#: barter.lua
msgid "Barter Table"
msgstr "Таблица бартера"
#: craftitems.lua
msgid "1 MineGeld Note"
msgstr "Банкнота в 1 MineGeld"
#: craftitems.lua
msgid "5 MineGeld Note"
msgstr "Банкнота в 5 MineGeld"
#: craftitems.lua
msgid "10 MineGeld Note"
msgstr "Банкнота в 10 MineGeld"
#: craftitems.lua
#, fuzzy
msgid "50 MineGeld Note"
msgstr "Банкнота в 5 MineGeld"
#: craftitems.lua
msgid "Bundle of random Minegeld notes"
msgstr "Пачка случайных банкнот Minegeld"
#: income.lua
msgid "basic income for @1"
msgstr "базовая прибыль для @1"
#: income.lua
msgid "added basic income for @1 to inventory"
msgstr "базовая прибыль для @1 добавлена в инвентарь"
#: init.lua
msgid "Currency mod loading..."
msgstr "Загружается мод Currency..."
#: init.lua
msgid "Craft_items Loaded!"
msgstr "Предметы для сборки загружены!"
#: init.lua
msgid "Shop Loaded!"
msgstr "Магазин загружен!"
#: init.lua
msgid "Barter Loaded!"
msgstr "Бартер загружен!"
#: init.lua
msgid "Safe Loaded!"
msgstr "Сейф загружен!"
#: init.lua
msgid "Crafting Loaded!"
msgstr "Механизм сборки загружен!"
#: init.lua
msgid "Creative mode in use, skipping basic income."
msgstr "Используется режим творчества, пропускаем базовую прибыль."
#: init.lua
msgid "Income Loaded!"
msgstr "Прибыль загружена!"
#: safe.lua
msgid "Safe"
msgstr "Сейф"
#: safe.lua
msgid "Safe (owned by @1)"
msgstr "Сейф (принадлежит @1)"
#: safe.lua
msgid "@1 tried to access a safe belonging to @2 at @3"
msgstr "@1 попробовал открыть сейф, принадлежащий @2 в @3"
#: safe.lua
msgid "@1 moves stuff in safe at @2"
msgstr "@1 перемещает вещи в сейфе в @2"
#: safe.lua
msgid "@1 moves stuff to safe at @2"
msgstr "@1 кладёт вещи в сейф в @2"
#: safe.lua
msgid "@1 takes stuff from safe at @2"
msgstr "@1 забирает вещи из сейфа в @2"
#: shop.lua
msgid "Customer gives (pay here!)"
msgstr "Покупатель отдаёт (здесь оплата!)"
#: shop.lua
msgid "Customer gets:"
msgstr "Покупатель получает:"
#: shop.lua
msgid "Owner wants:"
msgstr "Владелец хочет:"
#: shop.lua
msgid "Owner gives:"
msgstr "Владелец отдаёт:"
#: shop.lua
msgid "Customers gave:"
msgstr "Покупатель отдал:"
#: shop.lua
msgid "Your stock:"
msgstr "Ваш запас:"
#: shop.lua
msgid "You want:"
msgstr "Вы хотите:"
#: shop.lua
msgid "In exchange, you give:"
msgstr "Взамен вы даёте:"
#: shop.lua
msgid "Owner, Use (E)+Place (right mouse button) for customer interface"
msgstr ""
"Владелец, используйте (E)+Разместить (правая кнопка мыши) для открытия "
"интерфейса покупателя"
#: shop.lua
msgid "Shop"
msgstr "Магазин"
#: shop.lua
msgid "Exchange shop (owned by @1)"
msgstr "Пункт обмена (принадлежит @1)"
#: shop.lua
msgid "This is your own shop, you can't exchange to yourself!"
msgstr "Это ваш собственный магазин, вы не можете обмениваться с самим собой!"
#: shop.lua
msgid "Exchanged!"
msgstr "Обмен произведён!"
#: shop.lua
msgid "Exchange can not be done, contact the shop owner."
msgstr "Обмен невозможен, свяжитесь с владельцем магазина."
#: shop.lua
msgid "Exchange can not be done, check if you put all items!"
msgstr "Обмен невозможен, проверьте, что вы положили все предметы!"

View File

@ -1,182 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-05-24 23:06+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: barter.lua
msgid "Start"
msgstr ""
#: barter.lua
msgid "Confirm"
msgstr ""
#: barter.lua
msgid "Cancel"
msgstr ""
#: barter.lua shop.lua
msgid "Exchange"
msgstr ""
#: barter.lua
msgid "Barter Table"
msgstr ""
#: craftitems.lua
msgid "1 MineGeld Note"
msgstr ""
#: craftitems.lua
msgid "5 MineGeld Note"
msgstr ""
#: craftitems.lua
msgid "10 MineGeld Note"
msgstr ""
#: craftitems.lua
msgid "50 MineGeld Note"
msgstr ""
#: craftitems.lua
msgid "Bundle of random Minegeld notes"
msgstr ""
#: income.lua
msgid "basic income for @1"
msgstr ""
#: income.lua
msgid "added basic income for @1 to inventory"
msgstr ""
#: init.lua
msgid "Currency mod loading..."
msgstr ""
#: init.lua
msgid "Craft_items Loaded!"
msgstr ""
#: init.lua
msgid "Shop Loaded!"
msgstr ""
#: init.lua
msgid "Barter Loaded!"
msgstr ""
#: init.lua
msgid "Safe Loaded!"
msgstr ""
#: init.lua
msgid "Crafting Loaded!"
msgstr ""
#: init.lua
msgid "Creative mode in use, skipping basic income."
msgstr ""
#: init.lua
msgid "Income Loaded!"
msgstr ""
#: safe.lua
msgid "Safe"
msgstr ""
#: safe.lua
msgid "Safe (owned by @1)"
msgstr ""
#: safe.lua
msgid "@1 tried to access a safe belonging to @2 at @3"
msgstr ""
#: safe.lua
msgid "@1 moves stuff in safe at @2"
msgstr ""
#: safe.lua
msgid "@1 moves stuff to safe at @2"
msgstr ""
#: safe.lua
msgid "@1 takes stuff from safe at @2"
msgstr ""
#: shop.lua
msgid "Customer gives (pay here!)"
msgstr ""
#: shop.lua
msgid "Customer gets:"
msgstr ""
#: shop.lua
msgid "Owner wants:"
msgstr ""
#: shop.lua
msgid "Owner gives:"
msgstr ""
#: shop.lua
msgid "Customers gave:"
msgstr ""
#: shop.lua
msgid "Your stock:"
msgstr ""
#: shop.lua
msgid "You want:"
msgstr ""
#: shop.lua
msgid "In exchange, you give:"
msgstr ""
#: shop.lua
msgid "Owner, Use (E)+Place (right mouse button) for customer interface"
msgstr ""
#: shop.lua
msgid "Shop"
msgstr ""
#: shop.lua
msgid "Exchange shop (owned by @1)"
msgstr ""
#: shop.lua
msgid "This is your own shop, you can't exchange to yourself!"
msgstr ""
#: shop.lua
msgid "Exchanged!"
msgstr ""
#: shop.lua
msgid "Exchange can not be done, contact the shop owner."
msgstr ""
#: shop.lua
msgid "Exchange can not be done, check if you put all items!"
msgstr ""

View File

@ -0,0 +1,44 @@
# textdomain: currency
### barter.lua ###
Barter Table=
Cancel=
Confirm=
Start=
### shop.lua ###
Exchange=
### craftitems.lua ###
@1 Minegeld Note=
@1 Minegeld cent coin=
Bundle of random Minegeld notes=
### safe.lua ###
Safe=
Safe (owned by @1)=
### shop.lua ###
Customer gets:=
Customer gives (pay here!)=
Customers gave:=
Exchange can not be done, check if you put all items!=
Exchange can not be done, contact the shop owner.=
Exchange shop (owned by @1)=
Exchanged!=
In exchange, you give:=
Owner gives:=
Owner wants:=
Owner, Use (E)+Place (right mouse button) for customer interface=
Shop=
This is your own shop, you can't exchange to yourself!=
You want:=
Your stock:=

View File

@ -1,3 +1,4 @@
name = currency
depends = default
optional_depends = intllib,loot,pipeworks
optional_depends = loot, pipeworks
description = Provides shops, barter tables, safes, and multiple denominations of currency, called "Minegeld".

View File

@ -1,6 +1,4 @@
-- internationalization boilerplate
local MP = minetest.get_modpath(minetest.get_current_modname())
local S, NS = dofile(MP.."/intllib.lua")
local S = minetest.get_translator("currency")
function default.get_safe_formspec(pos)
local spos = pos.x .. "," .. pos.y .. "," ..pos.z
@ -60,8 +58,9 @@ minetest.register_node("currency:safe", {
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
local meta = minetest.get_meta(pos)
if not has_safe_privilege(meta, player) then
minetest.log("action", S("@1 tried to access a safe belonging to @2 at @3",
player:get_player_name(), meta:get_string("owner"), minetest.pos_to_string(pos)))
minetest.log("action", player:get_player_name().." tried to access a safe belonging to "
..meta:get_string("owner").." at "
..minetest.pos_to_string(pos))
return 0
end
return count
@ -69,8 +68,9 @@ minetest.register_node("currency:safe", {
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
if not has_safe_privilege(meta, player) then
minetest.log("action", S("@1 tried to access a safe belonging to @2 at @3",
player:get_player_name(), meta:get_string("owner"), minetest.pos_to_string(pos)))
minetest.log("action", player:get_player_name().." tried to access a safe belonging to "
..meta:get_string("owner").." at "
..minetest.pos_to_string(pos))
return 0
end
return stack:get_count()
@ -78,20 +78,21 @@ minetest.register_node("currency:safe", {
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
if not has_safe_privilege(meta, player) then
minetest.log("action", S("@1 tried to access a safe belonging to @2 at @3",
player:get_player_name(), meta:get_string("owner"), minetest.pos_to_string(pos)))
minetest.log("action", player:get_player_name().." tried to access a safe belonging to "
..meta:get_string("owner").." at "
..minetest.pos_to_string(pos))
return 0
end
return stack:get_count()
end,
on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
minetest.log("action", S("@1 moves stuff in safe at @2", player:get_player_name(), minetest.pos_to_string(pos)))
minetest.log("action", player:get_player_name().." moves stuff in safe at "..minetest.pos_to_string(pos))
end,
on_metadata_inventory_put = function(pos, listname, index, stack, player)
minetest.log("action", S("@1 moves stuff to safe at @2", player:get_player_name(), minetest.pos_to_string(pos)))
minetest.log("action", player:get_player_name().." moves stuff to safe at "..minetest.pos_to_string(pos))
end,
on_metadata_inventory_take = function(pos, listname, index, stack, player)
minetest.log("action", S("@1 takes stuff from safe at @2", player:get_player_name(), minetest.pos_to_string(pos)))
minetest.log("action", player:get_player_name().." takes stuff from safe at "..minetest.pos_to_string(pos))
end,
on_rightclick = function(pos, node, clicker)
local meta = minetest.get_meta(pos)

View File

@ -1,6 +1,4 @@
-- internationalization boilerplate
local MP = minetest.get_modpath(minetest.get_current_modname())
local S, NS = dofile(MP.."/intllib.lua")
local S = minetest.get_translator("currency")
default.shop = {}
default.shop.current_shop = {}
@ -27,10 +25,10 @@ default.shop.formspec = {
"list["..list_name..";customers_gave;0,0.5;3,2;]"..
"label[0,2.5;" .. S("Your stock:") .. "]"..
"list["..list_name..";stock;0,3;3,2;]"..
"label[5,0;" .. S("You want:") .. "]"..
"list["..list_name..";owner_wants;5,0.5;3,2;]"..
"label[5,2.5;" .. S("In exchange, you give:") .. "]"..
"list["..list_name..";owner_gives;5,3;3,2;]"..
"label[4,0;" .. S("You want:") .. "]"..
"list["..list_name..";owner_wants;4,0.5;3,2;]"..
"label[4,2.5;" .. S("In exchange, you give:") .. "]"..
"list["..list_name..";owner_gives;4,3;3,2;]"..
"label[0,5;" .. S("Owner, Use (E)+Place (right mouse button) for customer interface") .. "]"..
"list[current_player;main;0,5.5;8,4;]"
return formspec
@ -102,7 +100,7 @@ minetest.register_node("currency:shop", {
local owner = placer:get_player_name()
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Exchange shop (owned by @1)", owner))
meta:set_string("owner",owner)
meta:set_string("owner", owner)
--[[meta:set_string("pl1","")
meta:set_string("pl2","")]]
local inv = meta:get_inventory()
@ -166,7 +164,7 @@ minetest.register_on_player_receive_fields(function(sender, formname, fields)
local pos = default.shop.current_shop[name]
local meta = minetest.get_meta(pos)
if meta:get_string("owner") == name then
minetest.chat_send_player(name,S("This is your own shop, you can't exchange to yourself!"))
minetest.chat_send_player(name, S("This is your own shop, you can't exchange to yourself!"))
else
local minv = meta:get_inventory()
local pinv = sender:get_inventory()
@ -203,12 +201,12 @@ minetest.register_on_player_receive_fields(function(sender, formname, fields)
minv:remove_item("stock",item)
pinv:add_item("customer_gets",item)
end
minetest.chat_send_player(name,S("Exchanged!"))
minetest.chat_send_player(name, S("Exchanged!"))
else
if owners_fault then
minetest.chat_send_player(name,S("Exchange can not be done, contact the shop owner."))
minetest.chat_send_player(name, S("Exchange can not be done, contact the shop owner."))
else
minetest.chat_send_player(name,S("Exchange can not be done, check if you put all items!"))
minetest.chat_send_player(name, S("Exchange can not be done, check if you put all items!"))
end
end
end

View File

@ -71,3 +71,12 @@ As in, [digiline][junction box][dirt][junction box][digiline] will work to trans
How to use the I/O expander:
After setting a channel, send a table (same format as a Luacontroller's "port" table) to set the output states.
A table in this same format will be sent back whenever an input changes or you manually poll it by sending a "GET" message.
How to use the card reader:
After setting a channel, swiping a card (punch the reader with the card to swipe) will send a message in the following format:
{event = "read",data = "The data that was on the card"}
To write a card, send a command in the following format:
{command = "write",data = "The data to put on the card",description = "A description of what the card is for"}
After sending the write command, swipe the card to be written and the reader will send back the following message:
{event = "write"}
Both blank and previously written cards can be written to. If the card was not blank, it will be overwritten.

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