From 5101c3d00f60e746fa11cbc8b2c5b5eaf4bf87cb Mon Sep 17 00:00:00 2001 From: Joachim Stolberg Date: Sat, 29 Jan 2022 11:56:11 +0100 Subject: [PATCH] Add DE translation and 'drop item' option --- README.md | 11 +- i18n.py | 459 ++++++++++++++++++++++++++++++++++++++++ init.lua | 54 +++-- locale/lumberjack.de.tr | 16 ++ locale/template.txt | 17 ++ settingtypes.txt | 7 +- 6 files changed, 537 insertions(+), 27 deletions(-) create mode 100755 i18n.py create mode 100644 locale/lumberjack.de.tr create mode 100644 locale/template.txt diff --git a/README.md b/README.md index d43ba0f..8cdcf2d 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,10 @@ This mod fulfils several aspects: This mod allows to completely fell trees by destroying only one block. -The whole tree is harvested and moved to the players inventory. -But therefore some lumberjack skills are needed. New player normally will +The whole tree is harvested and moved to the player's inventory or +alternatively, the tree trunk items are dropped. See 'settingtypes.txt' +for configuration options. +But therefore, some lumberjack skills are needed. New player normally will not get the necessary skills immediately, they have to harvest the tree from the top, block by block "to improve their skills". @@ -52,7 +54,7 @@ Some technical aspects: default # License -Copyright (C) 2018-2021 Joachim Stolberg +Copyright (C) 2018-2022 Joachim Stolberg Code: Licensed under the GNU LGPL version 2.1 or later. See LICENSE.txt and http://www.gnu.org/licenses/lgpl-2.1.txt Sound is taken from Hybrid Dog (TreeCapitator) @@ -66,4 +68,5 @@ v0.5 - 17/Apr/2018 - protection bug fixed, further improvements v0.6 - 07/Jan/2020 - screwdriver bugfix v0.7 - 27/May/2020 - ethereal bugfix v0.8 - 29/Jul/2020 - fake player bugfix -v1.0 - 19/Mar/2021 - remove the lumberjack privs due to minetest engine issues \ No newline at end of file +v1.0 - 19/Mar/2021 - remove the lumberjack privs due to minetest engine issues +v1.1 - 29/Jan/2022 - Add DE translation and "drop item" option \ No newline at end of file diff --git a/i18n.py b/i18n.py new file mode 100755 index 0000000..e66de31 --- /dev/null +++ b/i18n.py @@ -0,0 +1,459 @@ +#!/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 +from sys import stderr as _stderr + +# Running params +params = {"recursive": False, + "help": False, + "mods": False, + "verbose": False, + "folders": [], + "no-old-file": False, + "break-long-lines": False, + "sort": False +} +# Available CLI options +options = {"recursive": ['--recursive', '-r'], + "help": ['--help', '-h'], + "mods": ['--installed-mods', '-m'], + "verbose": ['--verbose', '-v'], + "no-old-file": ['--no-old-file', '-O'], + "break-long-lines": ['--break-long-lines', '-b'], + "sort": ['--sort', '-s'] +} + +# 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 = 80 + +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["no-old-file"])} + do not create *.old files + {', '.join(options["sort"])} + sort output strings alphabetically + {', '.join(options["break-long-lines"])} + add extra line breaks before and after long strings + {', '.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_s = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL) +pattern_lua_fs = re.compile(r'[\.=^\t,{\(\s]N?FS\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL) +pattern_lua_bracketed_s = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*\[\[(.*?)\]\][\s,\)]', re.DOTALL) +pattern_lua_bracketed_fs = re.compile(r'[\.=^\t,{\(\s]N?FS\(\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, header_comments): + lOut = [f"# textdomain: {mod_name}\n"] + if header_comments is not None: + lOut.append(header_comments) + + dGroupedBySource = {} + + for key in dkeyStrings: + sourceList = list(dkeyStrings[key]) + if params["sort"]: + 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] + if params["sort"]: + 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 params["break-long-lines"] and len(localizedString) > doublespace_threshold and not lOut[-1] == "": + lOut.append("") + if comment != None: + lOut.append(comment) + lOut.append(f"{localizedString}={translation}") + if params["break-long-lines"] and 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 params["break-long-lines"] and len(key) > doublespace_threshold and not lOut[-1] == "": + lOut.append("") + if comment != None: + lOut.append(comment) + lOut.append(f"{key}={translation}") + if params["break-long-lines"] and 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, existing_template[2]) + 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_s.findall(text): + strings.append(s[1]) + for s in pattern_lua_bracketed_s.findall(text): + strings.append(s) + for s in pattern_lua_fs.findall(text): + strings.append(s[1]) + for s in pattern_lua_bracketed_fs.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. +# Returns also header comments in the third return value. +def import_tr_file(tr_file): + dOut = {} + text = None + header_comment = 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] == "###": + if header_comment is None: + # Save header comments + header_comment = latest_comment_block + # Stip textdomain line + tmp_h_c = "" + for l in header_comment.split('\n'): + if not l.startswith("# textdomain:"): + tmp_h_c += l + '\n' + header_comment = tmp_h_c + + # 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, header_comment) + +# 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, tr_import[2]) + + if textOld and textOld != textNew: + print(f"{tr_file} has changed.") + if not params["no-old-file"]: + 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(f"\033[31mUnable to find modname in folder {folder}.\033[0m", file=_stderr) + exit(1) + +# 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 + "/") + + +_argv.append("--sort") +main() diff --git a/init.lua b/init.lua index d3d297f..8c8781e 100644 --- a/init.lua +++ b/init.lua @@ -3,14 +3,14 @@ lumberjack ========== - Copyright (C) 2018-2021 Joachim Stolberg + Copyright (C) 2018-2022 Joachim Stolberg LGPLv2.1+ See LICENSE.txt for more information Mod to completely cut trees by destroying only one block. - This mod allows to destroy the bottom of the tree and the whole tree is felled - and moved to the players inventory. + This mod allows to destroy the root block of the tree and the whole tree is felled and + alternatively moved to the player's inventory or dropped. To distinguish between "grown" trees and placed tree nodes, the attribute 'node.param1' is used to identify placed nodes. @@ -23,12 +23,14 @@ lumberjack = {} -- Test MT 5.4 new string mode local CLIP = minetest.features.use_texture_alpha_string_modes and "clip" or true +local S = minetest.get_translator("lumberjack") local MY_PARAM1_VAL = 7 -- to identify placed nodes -- Necessary number of points for dug trees and placed sapling to get lumberjack privs local LUMBERJACK_TREE_POINTS = tonumber(minetest.settings:get("lumberjack_points")) or 400 local LUMBERJACK_SAPL_POINTS = math.floor(LUMBERJACK_TREE_POINTS / 6) +local DROP_ITEMS = minetest.settings:get_bool("lumberjack_drop_tree_items") == true local lTrees = {} -- List of registered tree items @@ -102,6 +104,9 @@ local function remove_items(pos1, pos2, name) for _,pos in ipairs(minetest.find_nodes_in_area(pos1, pos2, name)) do minetest.remove_node(pos) remove_steps(pos) + if DROP_ITEMS then + minetest.add_item(pos, ItemStack(name)) + end cnt = cnt + 1 end return cnt @@ -157,8 +162,8 @@ local function is_lumberjack(player, tree_points, sapl_points) if not meta:contains("is_lumberjack") then meta:set_int("is_lumberjack", 1) local player_name = player:get_player_name() - minetest.chat_send_player(player_name, "You're a real lumberjack now!") - minetest.log("action", player_name.." got lumberjack privs") + minetest.chat_send_player(player_name, S("You're a real lumberjack now!")) + minetest.log("action", player_name .. " got lumberjack privs") end end return true @@ -214,12 +219,14 @@ end -- Add tree items to the players inventory -- local function add_to_inventory(digger, name, len, pos) - local inv = digger:get_inventory() - local items = ItemStack(name .. " " .. len) - if inv and items and inv:room_for_item("main", items) then - inv:add_item("main", items) - else - minetest.item_drop(items, digger, pos) + if not DROP_ITEMS then + local inv = digger:get_inventory() + local items = ItemStack(name .. " " .. len) + if inv and items and inv:room_for_item("main", items) then + inv:add_item("main", items) + else + minetest.item_drop(items, digger, pos) + end end end @@ -276,6 +283,9 @@ local function can_dig(pos, digger) if is_lumberjack(digger, tree_points, sapl_points) then if chopper_tool(digger) then return true + else + minetest.chat_send_player(name, S("[Lumberjack Mod] You are using the wrong tool")) + return false end end local node = minetest.get_node(pos) @@ -285,7 +295,7 @@ local function can_dig(pos, digger) if is_top_tree_node(pos, node.name) then return true end - minetest.chat_send_player(name, "[Lumberjack Mod] From the top, please") + minetest.chat_send_player(name, S("[Lumberjack Mod] From the top, please")) return false end @@ -316,8 +326,8 @@ minetest.register_node("lumberjack:step", { -- Register the tree node to the lumberjack mod. -- 'tree_name' is the tree item name,, e.g. "default:tree" -- 'sapling_name' is the tree sapling name, e.g. "default:sapling" --- 'radius' the the range in nodes (+x/-x/+z/-z), where all available tree nodes will be removed. --- 'stem_height_min' is the minimum number of tree nodes, to be a valid stem (and not the a root item). +-- 'radius' is the range in nodes (+x/-x/+z/-z), where all available tree nodes will be removed. +-- 'stem_height_min' is the minimum number of tree nodes, to be a valid stem (and not a root item). -- function lumberjack.register_tree(tree_name, sapling_name, radius, stem_height_min) @@ -358,24 +368,24 @@ function lumberjack.register_tree(tree_name, sapling_name, radius, stem_height_m end minetest.register_chatcommand("lumberjack", { - description = "Output your lumberjack points", + description = S("Output your lumberjack points"), func = function(name, param) local tree_points, sapl_points = get_points(minetest.get_player_by_name(name)) if tree_points > 0 and sapl_points > 0 then - return true, "You need further " .. tree_points .. " tree " .. " and " .. sapl_points .. " sapling points." + return true, S("You need further @1 tree and @2 sapling points.", tree_points, sapl_points) elseif tree_points > 0 then - return true, "You need further " .. tree_points .. " tree points." + return true, S("You need further @1 tree points.", tree_points) elseif sapl_points > 0 then - return true, "You need further " .. sapl_points .. " sapling points." + return true, S("You need further @1 sapling points.", sapl_points) else - return true, "You are already a lumberjack." + return true, S("You are already a lumberjack.") end end }) minetest.register_chatcommand("set_lumberjack_points", { params = " ", - description = "Give a player lumberjack points", + description = S("Give a player lumberjack points"), privs = {server = true}, func = function(name, param) local param_name, points = param:match("^(%S+)%s+(%d+)$") @@ -388,9 +398,9 @@ minetest.register_chatcommand("set_lumberjack_points", { meta:set_int("lumberjack_tree_points", tree_points) meta:set_int("lumberjack_sapl_points", sapl_points) meta:set_string("is_lumberjack", "") - return true, "Player " .. param_name .. " now has " .. tree_points .. " tree and " .. sapl_points .. " sapling points." + return true, S("Player @1 now has @2 tree and @3 sapling points.", param_name, tree_points, sapl_points) end - return true, "Player " .. param_name .. "is unknown!" + return true, S("Player @1 is unknown!", param_name) end return false end diff --git a/locale/lumberjack.de.tr b/locale/lumberjack.de.tr new file mode 100644 index 0000000..731988b --- /dev/null +++ b/locale/lumberjack.de.tr @@ -0,0 +1,16 @@ +# textdomain: lumberjack + + +### init.lua ### + +Give a player lumberjack points=Gibt einem Spieler Holzfällerpunkte +Output your lumberjack points=Holzfällerpunkte ausgeben +Player @1 is unknown!=Spieler @1 ist unbekannt! +Player @1 now has @2 tree and @3 sapling points.=Spieler @1 hat jetzt @2 Baum- und @3 Setzlingspunkte. +You are already a lumberjack.=Du bist schon ein Holzfäller. +You need further @1 sapling points.=Du benötigst weitere @1 Setzlingspunkte. +You need further @1 tree and @2 sapling points.=Du benötigst weitere @1 Baum- und @2 Setzlingspunkte. +You need further @1 tree points.=Du benötigst weitere @1 Baumpunkte. +You're a real lumberjack now!=Du bist jetzt ein richtiger Holzfäller! +[Lumberjack Mod] From the top, please=[Lumberjack Mod] Von oben bitte +[Lumberjack Mod] You are using the wrong tool=[Lumberjack Mod] Du verwendest das falsche Werkzeug diff --git a/locale/template.txt b/locale/template.txt new file mode 100644 index 0000000..21f510d --- /dev/null +++ b/locale/template.txt @@ -0,0 +1,17 @@ +# textdomain: lumberjack + + + +### init.lua ### + +Give a player lumberjack points= +Output your lumberjack points= +Player @1 is unknown!= +Player @1 now has @2 tree and @3 sapling points.= +You are already a lumberjack.= +You need further @1 sapling points.= +You need further @1 tree and @2 sapling points.= +You need further @1 tree points.= +You're a real lumberjack now!= +[Lumberjack Mod] From the top, please= +[Lumberjack Mod] You are using the wrong tool= diff --git a/settingtypes.txt b/settingtypes.txt index 86c9fc1..df74802 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -1,4 +1,9 @@ # Settings for Lumberjack privs # 0 = Every player has lumberjack privs from the beginning. # >0 = Player has to collect the number points to get lumberjack privs (default 400). -lumberjack_points (needed points to get lumberjack privs) int 400 \ No newline at end of file +lumberjack_points (needed points to get lumberjack privs) int 400 + +# Drop tree trunk items +# If activated (set to true), tree trunk items will be dropped. +# If deactivated (set to false), tree trunk items will be moved to the player's inventory. +lumberjack_drop_tree_items (drop tree trunk items) bool false \ No newline at end of file