add some translation notes

This commit is contained in:
FaceDeer 2020-02-20 23:22:10 -07:00
parent 0c1a2ee0b0
commit 8be6bd9c54
3 changed files with 424 additions and 79 deletions

View File

@ -192,7 +192,7 @@ local get_account_formspec = function(market, account)
formspec[#formspec+1] = "label[3,0;"..S("Inventory limit:").."\n" .. inventory_count.."/" .. market_def.inventory_limit .. "]" formspec[#formspec+1] = "label[3,0;"..S("Inventory limit:").."\n" .. inventory_count.."/" .. market_def.inventory_limit .. "]"
.. "tooltip[3,0;1.5,1;"..S("You can still receive purchased items if you've exceeded your inventory limit,\nbut you won't be able to transfer items from your personal inventory into\nthe market until you've emptied it back down below the limit again.").."]" .. "tooltip[3,0;1.5,1;"..S("You can still receive purchased items if you've exceeded your inventory limit,\nbut you won't be able to transfer items from your personal inventory into\nthe market until you've emptied it back down below the limit again.").."]"
end end
formspec[#formspec+1] = "label[4.9,0;Balance:\n" .. market_def.currency_symbol .. account.balance .. "]" formspec[#formspec+1] = "label[4.9,0;"..S("Balance:") .. "\n" .. market_def.currency_symbol .. account.balance .. "]"
.."tooltip[4.9,0;3.5,1;"..S("Enter the amount of currency you'd like to withdraw then click the 'Withdraw'\nbutton to convert it into items and transfer it to your personal inventory.").."]" .."tooltip[4.9,0;3.5,1;"..S("Enter the amount of currency you'd like to withdraw then click the 'Withdraw'\nbutton to convert it into items and transfer it to your personal inventory.").."]"
.."field[6.1,0.325;1,1;withdrawamount;;]" .."field[6.1,0.325;1,1;withdrawamount;;]"
.."field_close_on_enter[withdrawamount;false]" .."field_close_on_enter[withdrawamount;false]"

355
i18n.py
View File

@ -1,31 +1,125 @@
#!/usr/bin/env python #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Script to generate the template file and update the translation files. # 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. # Copy the script into the mod or modpack root folder and run it there.
# #
# Copyright (C) 2019 Joachim Stolberg # Copyright (C) 2019 Joachim Stolberg, 2020 FaceDeer, 2020 Louis Royer
# LGPLv2.1+ # LGPLv2.1+
from __future__ import print_function from __future__ import print_function
import os, fnmatch, re, shutil, errno 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 ') #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 #See https://stackoverflow.com/questions/46967465/regex-match-text-in-either-single-or-double-quote
#TODO: support [[]] delimiters
pattern_lua = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL) 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" # Handles "concatenation" .. " of strings"
pattern_concat = re.compile(r'["\'][\s]*\.\.[\s]*["\']', re.DOTALL) pattern_concat = re.compile(r'["\'][\s]*\.\.[\s]*["\']', re.DOTALL)
pattern_tr = re.compile(r'(.+?[^@])=(.+)') pattern_tr = re.compile(r'(.+?[^@])=(.*)')
pattern_name = re.compile(r'^name[ ]*=[ ]*([^ ]*)') pattern_name = re.compile(r'^name[ ]*=[ ]*([^ \n]*)')
pattern_tr_filename = re.compile(r'\.tr$') 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 #attempt to read the mod's name from the mod.conf file. Returns None on failure
def get_modname(folder): def get_modname(folder):
try: try:
with open(folder + "mod.conf", "r", encoding='utf-8') as mod_conf: with open(os.path.join(folder, "mod.conf"), "r", encoding='utf-8') as mod_conf:
for line in mod_conf: for line in mod_conf:
match = pattern_name.match(line) match = pattern_name.match(line)
if match: if match:
@ -37,12 +131,62 @@ def get_modname(folder):
#If there are already .tr files in /locale, returns a list of their names #If there are already .tr files in /locale, returns a list of their names
def get_existing_tr_files(folder): def get_existing_tr_files(folder):
out = [] out = []
for root, dirs, files in os.walk(folder + 'locale/'): for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
for name in files: for name in files:
if pattern_tr_filename.search(name): if pattern_tr_filename.search(name):
out.append(name) out.append(name)
return out 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 # from https://stackoverflow.com/questions/600268/mkdir-p-functionality-in-python/600612#600612
# Creates a directory if it doesn't exist, silently does # Creates a directory if it doesn't exist, silently does
# nothing if it already exists # nothing if it already exists
@ -54,24 +198,93 @@ def mkdir_p(path):
pass pass
else: raise 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 # Writes a template.txt file
def write_template(templ_file, lkeyStrings): # dkeyStrings is the dictionary returned by generate_template
lOut = [] def write_template(templ_file, dkeyStrings, mod_name):
lkeyStrings.sort() # read existing template file to preserve comments
for s in lkeyStrings: existing_template = import_tr_file(templ_file)
lOut.append("%s=" % s)
text = strings_to_text(dkeyStrings, existing_template[0], mod_name)
mkdir_p(os.path.dirname(templ_file)) mkdir_p(os.path.dirname(templ_file))
with open(templ_file, "wt", encoding='utf-8') as template_file: with open(templ_file, "wt", encoding='utf-8') as template_file:
template_file.write("\n".join(lOut)) template_file.write(text)
# Gets all translatable strings from a lua file # Gets all translatable strings from a lua file
def read_lua_file_strings(lua_file): def read_lua_file_strings(lua_file):
lOut = [] lOut = []
with open(lua_file, encoding='utf-8') as text_file: with open(lua_file, encoding='utf-8') as text_file:
text = text_file.read() text = text_file.read()
#TODO remove comments here
text = re.sub(pattern_concat, "", text) text = re.sub(pattern_concat, "", text)
strings = []
for s in pattern_lua.findall(text): for s in pattern_lua.findall(text):
s = s[1] 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(r'"\.\.\s+"', "", s)
s = re.sub("@[^@=0-9]", "@@", s) s = re.sub("@[^@=0-9]", "@@", s)
s = s.replace('\\"', '"') s = s.replace('\\"', '"')
@ -83,74 +296,112 @@ def read_lua_file_strings(lua_file):
return lOut return lOut
# Gets strings from an existing translation file # 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): def import_tr_file(tr_file):
dOut = {} dOut = {}
text = None
if os.path.exists(tr_file): if os.path.exists(tr_file):
with open(tr_file, "r", encoding='utf-8') as existing_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(): for line in existing_file.readlines():
s = line.strip() line = line.rstrip('\n')
if s == "" or s[0] == "#": if line[:3] == "###":
# Reset comment block if we hit a header
latest_comment_block = None
continue continue
match = pattern_tr.match(s) 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: if match:
dOut[match.group(1)] = match.group(2) # this line is a translated line
return dOut 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, # Walks all lua files in the mod folder, collects translatable strings,
# and writes it to a template.txt file # and writes it to a template.txt file
def generate_template(folder): # Returns a dictionary of localized strings to source file sets
lOut = [] # 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 root, dirs, files in os.walk(folder):
for name in files: for name in files:
if fnmatch.fnmatch(name, "*.lua"): if fnmatch.fnmatch(name, "*.lua"):
fname = os.path.join(root, name) fname = os.path.join(root, name)
found = read_lua_file_strings(fname) found = read_lua_file_strings(fname)
print(fname + ": " + str(len(found)) + " translatable strings") if params["verbose"]:
lOut.extend(found) print(f"{fname}: {str(len(found))} translatable strings")
lOut = list(set(lOut))
lOut.sort() for s in found:
if len(lOut) == 0: sources = dOut.get(s, set())
sources.add(f"### {os.path.basename(fname)} ###")
dOut[s] = sources
if len(dOut) == 0:
return None return None
templ_file = folder + "locale/template.txt" templ_file = os.path.join(folder, "locale/template.txt")
write_template(templ_file, lOut) write_template(templ_file, dOut, mod_name)
return lOut return dOut
# Updates an existing .tr file, copying the old one to a ".old" file # Updates an existing .tr file, copying the old one to a ".old" file
def update_tr_file(lNew, mod_name, tr_file): # if any changes have happened
print("updating " + tr_file) # dNew is the data used to generate the template, it has all the
lOut = ["# textdomain: %s\n" % mod_name] # currently-existing localized strings
def update_tr_file(dNew, mod_name, tr_file):
if params["verbose"]:
print(f"updating {tr_file}")
#TODO only make a .old if there are actual changes from the old file tr_import = import_tr_file(tr_file)
if os.path.exists(tr_file): dOld = tr_import[0]
shutil.copyfile(tr_file, tr_file+".old") 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")
dOld = import_tr_file(tr_file)
for key in lNew:
val = dOld.get(key, "")
lOut.append("%s=%s" % (key, val))
lOut.append("##### not used anymore #####")
for key in dOld:
if key not in lNew:
lOut.append("%s=%s" % (key, dOld[key]))
with open(tr_file, "w", encoding='utf-8') as new_tr_file: with open(tr_file, "w", encoding='utf-8') as new_tr_file:
new_tr_file.write("\n".join(lOut)) new_tr_file.write(textNew)
# Updates translation files for the mod in the given folder # Updates translation files for the mod in the given folder
def update_mod(folder): def update_mod(folder):
modname = get_modname(folder) modname = get_modname(folder)
if modname is not None: if modname is not None:
print("Updating translations for " + modname) process_po_files(folder, modname)
data = generate_template(folder) print(f"Updating translations for {modname}")
data = generate_template(folder, modname)
if data == None: if data == None:
print("No translatable strings found in " + modname) print(f"No translatable strings found in {modname}")
else: else:
for tr_file in get_existing_tr_files(folder): for tr_file in get_existing_tr_files(folder):
update_tr_file(data, modname, folder + "locale/" + tr_file) update_tr_file(data, modname, os.path.join(folder, "locale/", tr_file))
else: else:
print("Unable to find modname in folder " + folder) 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): def update_folder(folder):
is_modpack = os.path.exists(folder+"modpack.txt") or os.path.exists(folder+"modpack.conf") is_modpack = os.path.exists(os.path.join(folder, "modpack.txt")) or os.path.exists(os.path.join(folder, "modpack.conf"))
if is_modpack: if is_modpack:
subfolders = [f.path for f in os.scandir(folder) if f.is_dir()] subfolders = [f.path for f in os.scandir(folder) if f.is_dir()]
for subfolder in subfolders: for subfolder in subfolders:
@ -159,5 +410,9 @@ def update_folder(folder):
update_mod(folder) update_mod(folder)
print("Done.") 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 + "/")
update_folder("./")
main()

View File

@ -1,86 +1,176 @@
1 @1 @= @2@3= # textdomain: commoditymarket
A market where orders to buy or sell items can be placed and fulfilled.=
Add all registered items to the provided market=
All the items you've transfered to the market to sell and the items you've@npurchased with buy orders. Double-click on an item to bring it back into your@npersonal inventory.= ### doc.lua ###
Apply search to outputs.=
#long-form description of how markets work, for documentation
At the core of how a market operates are "buy" and "sell" orders. A buy order is an announcement to the world that you are interested in purchasing a certain quantity of item and are willing to pay a certain amount of currency in exchange for each unit of that item. Conversely, a sell order is an announcement to the world that you are interested in selling a certain quantity of item and will accept a certain amount of currency in exchange for each unit of that item.@n@nThe market price of an item is determined by where the existing buy and sell orders for that item intersect. When you offer to buy an item for a price that someone is offering to sell it at, the item is transferred to you and currency is transferred from your account to theirs to cover the cost. The market will keep track of the most recent price that an item was successfully sold for, but note that this information is for historical interest only - there's no guarantee that anyone is currently willing to match the historical price.@n@nWhen an item is selected in the upper list, the currently existing buy and sell orders for that item will be displayed in the lower list. Sell orders are listed first in descending price, followed by buy orders in ascending price. The current market price will be somewhere in between the lowest sell order and the highest buy order. If you wish to cancel a buy or sell order that you've placed for an item, double-click on the order and the item or currency that you put into that order will be returned to your inventory.@n@nIf you place a buy order and there are already sell orders for the item that meet or are below your price, some or all of your buy order might be immediately fulfilled. Your purchases will be made at the price that the sell orders have been set to - if you were willing to pay 15 units of currency per item but someone was already offering to sell for 2 units of currency per item, you only pay 2 units for each of that offer's items. If there aren't enough compatible sell orders to fulfill your buy order, the remainder will be placed into the market and made available for future sellers to see and fulfill if they agree to your price. Your buy order will immediately deduct the currency required for it from your account's balance, but if you cancel your order you will get that currency back - it's not gone until the order is actually fulfilled.@n@nIf you place a sell order and there are already buy orders that meet or exceed your price, some or all of your sell order may be immediately fulfilled. You'll be paid the price that the buyers are offering rather than the amount you're demanding. If any of your sell offer is left unfulfilled, the sell order will be added to the market for future buyers to see. The items for this offer will be immediately taken from your market inventory but if you cancel your order you will get those items back.= At the core of how a market operates are "buy" and "sell" orders. A buy order is an announcement to the world that you are interested in purchasing a certain quantity of item and are willing to pay a certain amount of currency in exchange for each unit of that item. Conversely, a sell order is an announcement to the world that you are interested in selling a certain quantity of item and will accept a certain amount of currency in exchange for each unit of that item.@n@nThe market price of an item is determined by where the existing buy and sell orders for that item intersect. When you offer to buy an item for a price that someone is offering to sell it at, the item is transferred to you and currency is transferred from your account to theirs to cover the cost. The market will keep track of the most recent price that an item was successfully sold for, but note that this information is for historical interest only - there's no guarantee that anyone is currently willing to match the historical price.@n@nWhen an item is selected in the upper list, the currently existing buy and sell orders for that item will be displayed in the lower list. Sell orders are listed first in descending price, followed by buy orders in ascending price. The current market price will be somewhere in between the lowest sell order and the highest buy order. If you wish to cancel a buy or sell order that you've placed for an item, double-click on the order and the item or currency that you put into that order will be returned to your inventory.@n@nIf you place a buy order and there are already sell orders for the item that meet or are below your price, some or all of your buy order might be immediately fulfilled. Your purchases will be made at the price that the sell orders have been set to - if you were willing to pay 15 units of currency per item but someone was already offering to sell for 2 units of currency per item, you only pay 2 units for each of that offer's items. If there aren't enough compatible sell orders to fulfill your buy order, the remainder will be placed into the market and made available for future sellers to see and fulfill if they agree to your price. Your buy order will immediately deduct the currency required for it from your account's balance, but if you cancel your order you will get that currency back - it's not gone until the order is actually fulfilled.@n@nIf you place a sell order and there are already buy orders that meet or exceed your price, some or all of your sell order may be immediately fulfilled. You'll be paid the price that the buyers are offering rather than the amount you're demanding. If any of your sell offer is left unfulfilled, the sell order will be added to the market for future buyers to see. The items for this offer will be immediately taken from your market inventory but if you cancel your order you will get those items back.=
Balance:=
Buy= #documentation tab text
Buy Max=
Buy Vol=
Clear search.=
Commodity Markets= Commodity Markets=
Currency item values:=
#long-form description of how markets work, for documentation
Each player's account has an inventory that serves as a holding area for items that are destined to be sold or that have been bought by the player but not yet retrieved. This inventory is a bit different from the standard Minetest inventory in that it doesn't hold item "stacks", it just tracks the total number of that item present. Some markets allow for extremely large quantities of an item to be stored here for sale.@n@nTo add an item to your market inventory for eventual sale either shift-click on the item in your player inventory or drag the item stack to the inventory slot below the main market inventory list. Some markets may have restrictions on what items can be bought and sold, if an item is not valid for that market it won't go into the market's inventory. Some items are considered "currency" and will add to your account's currency balance instead of being listed in your market inventory.@n@nTools cannot be added to the market inventory if they have any wear on them. The market also can't handle items with attached metadata such as books that have had text added to them.@n@nTo remove an item from your market inventory, double-click in it in the market inventory list. As much of the item as can fit into your player inventory will be transferred to you, with any remainder staying behind in the market inventory. To withdraw currency from your market balance type the amount you'd like to withdraw in the field next to the "Withdraw" button. The currency will be converted into items and added to your player inventory, with whatever cannot be converted remaining behind in your market balance.=
#documentation category description
Game-wide marketplaces where goods can be bought and sold at prices of your choice.=
#title of documentation page
User Interface: Inventory=
#title of documentation page
User Interface: Orders=
### formspecs.lua ###
#tooltip
All the items you've transfered to the market to sell and the items you've@npurchased with buy orders. Double-click on an item to bring it back into your@npersonal inventory.=
#tooltip
Apply search to outputs.=
Balance:=
#label
#button label
Buy=
#table column header
Buy Max=
#table column header
Buy Vol=
#tooltip
Clear search.=
Days Old= Days Old=
#table column header
Description= Description=
Description:= Description:=
#label for inventory drop target. The hard return is needed to fit it into the UI
Drop items here to@nadd to your account= Drop items here to@nadd to your account=
Each player's account has an inventory that serves as a holding area for items that are destined to be sold or that have been bought by the player but not yet retrieved. This inventory is a bit different from the standard Minetest inventory in that it doesn't hold item "stacks", it just tracks the total number of that item present. Some markets allow for extremely large quantities of an item to be stored here for sale.@n@nTo add an item to your market inventory for eventual sale either shift-click on the item in your player inventory or drag the item stack to the inventory slot below the main market inventory list. Some markets may have restrictions on what items can be bought and sold, if an item is not valid for that market it won't go into the market's inventory. Some items are considered "currency" and will add to your account's currency balance instead of being listed in your market inventory.@n@nTools cannot be added to the market inventory if they have any wear on them. The market also can't handle items with attached metadata such as books that have had text added to them.@n@nTo remove an item from your market inventory, double-click in it in the market inventory list. As much of the item as can fit into your player inventory will be transferred to you, with any remainder staying behind in the market inventory. To withdraw currency from your market balance type the amount you'd like to withdraw in the field next to the "Withdraw" button. The currency will be converted into items and added to your player inventory, with whatever cannot be converted remaining behind in your market balance.= #tooltip
Enter substring to search item identifiers for.= Enter substring to search item identifiers for.=
#tooltip
Enter the amount of currency you'd like to withdraw then click the 'Withdraw'@nbutton to convert it into items and transfer it to your personal inventory.= Enter the amount of currency you'd like to withdraw then click the 'Withdraw'@nbutton to convert it into items and transfer it to your personal inventory.=
Game-wide marketplaces where goods can be bought and sold at prices of your choice.=
How many days ago this order was placed.= How many days ago this order was placed.=
#label
In inventory:= In inventory:=
#table column header
Inventory= Inventory=
#label
Inventory limit:= Inventory limit:=
#table column header
Item= Item=
#table column header
Last Price= Last Price=
#tooltip
Log entries in yellow are new since last time you marked your log as read.= Log entries in yellow are new since last time you marked your log as read.=
Mark logs as read= Mark logs as read=
Market= #tab label
Market Orders= Market Orders=
Market has unlimited inventory space.= #tooltip
Market inventory is limited to @1 items.=
Market supports unlimited pending sell orders.=
Maximum price being offered to buy one of these.= Maximum price being offered to buy one of these.=
#tooltip
Minimum price being demanded to sell one of these.= Minimum price being demanded to sell one of these.=
#checkbox label
My orders= My orders=
#shown if there are no transactions the player has participated in to list in the log
No logged activities in this market yet.= No logged activities in this market yet.=
#tooltip
Number of items available for sale in the market.= Number of items available for sale in the market.=
#tooltip
Number of items there's demand for in the market.= Number of items there's demand for in the market.=
#transaction log entry. @1 is a day number, @2 is a player name, @3 is a quantity of items, @4 is the item name, @5 is a player name, @6 is a currency symbol, @7 is a number (price), and @8 is a number (price)
On day @1 @2 sold @3 @4 to @5 at @6@7 each for a total of @6@8.= On day @1 @2 sold @3 @4 to @5 at @6@7 each for a total of @6@8.=
Order= Order=
Player= Player=
Price= Price=
#tooltip
Price paid for one of these the last time one was sold.= Price paid for one of these the last time one was sold.=
Price per= Price per=
Purging item: @1 from market: @2= #table column header, field label
Quantity= Quantity=
#tooltip
Quantity of this item that you have in your inventory ready to sell.= Quantity of this item that you have in your inventory ready to sell.=
Select an item to view or place orders.= Select an item to view or place orders.=
#tooltip
Select this to show only the markets where you have either a buy or a sell order pending.= Select this to show only the markets where you have either a buy or a sell order pending.=
Sell= Sell=
#table column header
Sell Min= Sell Min=
#table column header
Sell Vol= Sell Vol=
Sell limit:= Sell limit:=
#checkbox label
Show Icons= Show Icons=
#checkbox label
Show Itemnames= Show Itemnames=
The name of the player who placed this order.@nDouble-click your own orders to cancel them.= The name of the player who placed this order.@nDouble-click your own orders to cancel them.=
The price per item in this order.= The price per item in this order.=
The total amount of items available at this price accounting for the other orders also currently being offered.= The total amount of items available at this price accounting for the other orders also currently being offered.=
The total amount of items in this particular order.= The total amount of items in this particular order.=
#tooltip
This market limits the total number of items a given seller can have for sale at a time.@nYou have @1 items remaining. Cancel old sell orders to free up space.= This market limits the total number of items a given seller can have for sale at a time.@nYou have @1 items remaining. Cancel old sell orders to free up space.=
Total Volume= Total Volume=
Total pending sell orders are limited to @1 items.= #undefined item
Unknown Item= Unknown Item=
#tooltip
Use these fields to enter buy and sell orders for the selected item.= Use these fields to enter buy and sell orders for the selected item.=
User Interface: Inventory=
User Interface: Orders= #button label
Withdraw= Withdraw=
#tooltip
You can still receive purchased items if you've exceeded your inventory limit,@nbut you won't be able to transfer items from your personal inventory into@nthe market until you've emptied it back down below the limit again.= You can still receive purchased items if you've exceeded your inventory limit,@nbut you won't be able to transfer items from your personal inventory into@nthe market until you've emptied it back down below the limit again.=
#tab label
Your Inventory=
Your Recent Purchases and Sales:=
someone=
you=
yourself=
### market.lua ###
1 @1 @= @2@3=
A market where orders to buy or sell items can be placed and fulfilled.=
Add all registered items to the provided market=
Currency item values:=
Market=
Market has unlimited inventory space.=
Market inventory is limited to @1 items.=
Market supports unlimited pending sell orders.=
Purging item: @1 from market: @2=
Total pending sell orders are limited to @1 items.=
You can't afford that many of this item.= You can't afford that many of this item.=
You can't pay less than nothing for an item.= You can't pay less than nothing for an item.=
You can't sell fewer than one item.= You can't sell fewer than one item.=
You can't sell items for a negative price.= You can't sell items for a negative price.=
You don't have enough of that item in your inventory to post this sell order.= You don't have enough of that item in your inventory to post this sell order.=
You have to buy at least one item.= You have to buy at least one item.=
You have too many items listed for sale in this market, please cancel some sell orders to make room for new ones.= You have too many items listed for sale in this market, please cancel some sell orders to make room for new ones.=
Your Inventory=
Your Recent Purchases and Sales:=
list all registered markets= list all registered markets=
remove item from market. All existing buys and sells will be canceled.= remove item from market. All existing buys and sells will be canceled.=
removes all unknown items from all markets. All existing buys and sells for those items will be canceled.= removes all unknown items from all markets. All existing buys and sells for those items will be canceled.=
show market interface= show market interface=
someone=
you=
yourself=