From 1e776defd5378c30b7774a1022cfe02bba9b4618 Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sat, 22 Feb 2020 13:51:41 -0700 Subject: [PATCH] update to client-side translation system --- controller.lua | 17 +- depends.txt | 3 - functions.lua | 4 +- i18n.py | 418 +++++++++++++++++++++++++++++ init.lua | 5 +- intllib.lua | 45 ---- locale/template.pot | 276 ------------------- locale/template.txt | 149 ++++++++++ locale/update.bat | 6 - mod.conf | 2 +- nodes/node_builder.lua | 4 +- nodes/node_digger.lua | 4 +- nodes/node_duplicator.lua | 17 +- nodes/node_misc.lua | 4 +- nodes/node_pipeworks_interface.lua | 134 +++++++++ nodes/node_storage.lua | 4 +- nodes/recipes.lua | 4 +- 17 files changed, 723 insertions(+), 373 deletions(-) delete mode 100644 depends.txt create mode 100644 i18n.py delete mode 100644 intllib.lua delete mode 100644 locale/template.pot create mode 100644 locale/template.txt delete mode 100644 locale/update.bat create mode 100644 nodes/node_pipeworks_interface.lua diff --git a/controller.lua b/controller.lua index 8bd1c6f..af9b931 100644 --- a/controller.lua +++ b/controller.lua @@ -1,6 +1,4 @@ --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") +local S = digtron.S local position_and_anchor = "position[0.025,0.1]anchor[0,0]" @@ -100,13 +98,13 @@ decrement_sequence = function(sequence, target_item) -- return without decrementing its parent return "found" elseif command == target_item then - target_item.cur = target_item.cur - 1 - found = true + target_item.cur = target_item.cur - 1 -- decrement the remaining count on the target item + found = true -- note that we've found the target item. elseif command.cmd == "seq" then - local subsequence_result = decrement_sequence(command, target_item) + local subsequence_result = decrement_sequence(command, target_item) -- recurse if subsequence_result == "decrement_parent" then -- the item was in the subsequence and the subsequence's list of commands are finished - -- so decrement the subsequence and reset its constituents + -- so decrement the subsequence's current count and reset its constituents to their defaults command.cur = command.cur - 1 for _, subcommand in ipairs(command.seq) do subcommand.cur = subcommand.cnt @@ -123,6 +121,7 @@ decrement_sequence = function(sequence, target_item) end end +-- recurses through the whole sequence setting all current counts to the base value local reset_sequence reset_sequence = function(sequence) for _, command in ipairs(sequence.seq) do @@ -350,7 +349,7 @@ end local sequence_tab = function(digtron_id) local sequence = digtron.get_sequence(digtron_id) - local list_out = {"size[5.75,6.75]" + local list_out = {"size[5.75,12]" .. position_and_anchor .. "real_coordinates[true]" @@ -797,7 +796,7 @@ minetest.register_node("digtron:controller_unassembled", combine_defs(base_def, local meta = minetest.get_meta(pos) meta:set_string("digtron_id", digtron_id) meta:mark_as_private("digtron_id") - player_opening_formspec(digtron_id, player_name) + get_context(digtron_id, player_name) minetest.show_formspec(player_name, "digtron:controller_assembled", get_controller_assembled_formspec(digtron_id, player_name)) diff --git a/depends.txt b/depends.txt deleted file mode 100644 index d5c409f..0000000 --- a/depends.txt +++ /dev/null @@ -1,3 +0,0 @@ -default -intllib? -creative? \ No newline at end of file diff --git a/functions.lua b/functions.lua index 213189f..e24393b 100644 --- a/functions.lua +++ b/functions.lua @@ -2,9 +2,7 @@ local mod_meta = digtron.mod_meta local cache = {} --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") +local S = digtron.S --minetest.debug(dump(mod_meta:to_table())) diff --git a/i18n.py b/i18n.py new file mode 100644 index 0000000..d33bbb0 --- /dev/null +++ b/i18n.py @@ -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() diff --git a/init.lua b/init.lua index 35f150a..f7d2ab2 100644 --- a/init.lua +++ b/init.lua @@ -1,6 +1,9 @@ digtron = {} digtron.doc = {} -- TODO: move to doc file +local modname = minetest.get_current_modname() +digtron.S = minetest.get_translator(modname) + -- A global dictionary is used here so that other substitutions can be added easily by other mods, if necessary digtron.builder_read_item_substitutions = { ["default:torch_ceiling"] = "default:torch", @@ -36,7 +39,7 @@ digtron.builder_on_place_prefixes = { digtron.mod_meta = minetest.get_mod_storage() -local modpath = minetest.get_modpath(minetest.get_current_modname()) +local modpath = minetest.get_modpath(modname) dofile(modpath.."/config.lua") dofile(modpath.."/class_fakeplayer.lua") diff --git a/intllib.lua b/intllib.lua deleted file mode 100644 index 6669d72..0000000 --- a/intllib.lua +++ /dev/null @@ -1,45 +0,0 @@ - --- Fallback functions for when `intllib` is not installed. --- Code released under Unlicense . - --- 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 diff --git a/locale/template.pot b/locale/template.pot deleted file mode 100644 index 49e49d1..0000000 --- a/locale/template.pot +++ /dev/null @@ -1,276 +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 , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-09-01 18:19-0600\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" -"Content-Transfer-Encoding: 8bit\n" - -#: digtron\controller.lua:7 -msgid "Main Inventory" -msgstr "" - -#: digtron\controller.lua:8 -msgid "Fuel" -msgstr "" - -#: digtron\controller.lua:13 -msgid "Sequence" -msgstr "" - -#: digtron\controller.lua:14 -msgid "Dig Move Build" -msgstr "" - -#: digtron\controller.lua:15 -msgid "Dig Move Down" -msgstr "" - -#: digtron\controller.lua:16 -msgid "Move Up" -msgstr "" - -#: digtron\controller.lua:17 -msgid "Move Down" -msgstr "" - -#: digtron\controller.lua:18 -msgid "Move Left" -msgstr "" - -#: digtron\controller.lua:19 -msgid "Move Right" -msgstr "" - -#: digtron\controller.lua:20 -msgid "Move Forward" -msgstr "" - -#: digtron\controller.lua:21 -msgid "Move Back" -msgstr "" - -#: digtron\controller.lua:22 -msgid "Yaw Left" -msgstr "" - -#: digtron\controller.lua:23 -msgid "Yaw Right" -msgstr "" - -#: digtron\controller.lua:24 -msgid "Pitch Up" -msgstr "" - -#: digtron\controller.lua:25 -msgid "Pitch Down" -msgstr "" - -#: digtron\controller.lua:26 -msgid "Roll Clockwise" -msgstr "" - -#: digtron\controller.lua:27 -msgid "Roll Widdershins" -msgstr "" - -#: digtron\controller.lua:402 -msgid "Digtron Control Module" -msgstr "" - -#: digtron\controller.lua:436 -msgid "Digtron Assembly" -msgstr "" - -#: digtron\functions.lua:65 -msgid "Unnamed Digtron" -msgstr "" - -#: digtron\functions.lua:1012 -msgid "@1 at @2 has encountered an obstacle." -msgstr "" - -#: digtron\functions.lua:1023 -msgid "@1 at @2 requires @3 to execute its next build cycle." -msgstr "" - -#: digtron\nodes\node_builder.lua:25 -msgid "Extrusion" -msgstr "" - -#: digtron\nodes\node_builder.lua:27 -msgid "" -"Builder will extrude this many blocks in the direction it is facing.\n" -"Can be set from 1 to @1.\n" -"Note that Digtron won't build into unloaded map regions." -msgstr "" - -#: digtron\nodes\node_builder.lua:28 -#: digtron\nodes\node_digger.lua:20 -msgid "Periodicity" -msgstr "" - -#: digtron\nodes\node_builder.lua:30 -msgid "" -"Builder will build once every n steps.\n" -"These steps are globally aligned, so all builders with the\n" -"same period and offset will build on the same location." -msgstr "" - -#: digtron\nodes\node_builder.lua:31 -#: digtron\nodes\node_digger.lua:23 -msgid "Offset" -msgstr "" - -#: digtron\nodes\node_builder.lua:33 -msgid "" -"Offsets the start of periodicity counting by this amount.\n" -"For example, a builder with period 2 and offset 0 builds\n" -"every even-numbered block and one with period 2 and\n" -"offset 1 builds every odd-numbered block." -msgstr "" - -#: digtron\nodes\node_builder.lua:34 -#: digtron\nodes\node_digger.lua:26 -msgid "" -"Save &\n" -"Show" -msgstr "" - -#: digtron\nodes\node_builder.lua:35 -msgid "" -"Saves settings, closes interface, and shows the locations this builder will " -"build to in-world." -msgstr "" - -#: digtron\nodes\node_builder.lua:36 -msgid "Facing" -msgstr "" - -#: digtron\nodes\node_builder.lua:38 -msgid "" -"Value from 0-23. Not all block types make use of this.\n" -"Use the 'Read & Save' button to copy the facing of the block\n" -"currently in the builder output location." -msgstr "" - -#: digtron\nodes\node_builder.lua:39 -msgid "Read" -msgstr "" - -#: digtron\nodes\node_builder.lua:40 -msgid "Reads the facing of the block currently in the build location." -msgstr "" - -#: digtron\nodes\node_builder.lua:130 -#: digtron\nodes\node_storage.lua:73 -#: digtron\nodes\node_storage.lua:178 -#: digtron\nodes\node_storage.lua:305 -msgid "This Digtron is active, interact with it via the controller node." -msgstr "" - -#: digtron\nodes\node_builder.lua:223 -msgid "" -"Builder for @1\n" -"period @2, offset @3, extrusion @4" -msgstr "" - -#: digtron\nodes\node_builder.lua:232 -msgid "Digtron Builder Module" -msgstr "" - -#: digtron\nodes\node_digger.lua:22 -msgid "" -"Digger will dig once every n steps.\n" -"These steps are globally aligned, all diggers with\n" -"the same period and offset will dig on the same location." -msgstr "" - -#: digtron\nodes\node_digger.lua:25 -msgid "" -"Offsets the start of periodicity counting by this amount.\n" -"For example, a digger with period 2 and offset 0 digs\n" -"every even-numbered block and one with period 2 and\n" -"offset 1 digs every odd-numbered block." -msgstr "" - -#: digtron\nodes\node_digger.lua:27 -msgid "Saves settings" -msgstr "" - -#: digtron\nodes\node_digger.lua:35 -msgid "" -"Digger\n" -"period @1, offset @2" -msgstr "" - -#: digtron\nodes\node_digger.lua:71 -#: digtron\nodes\node_digger.lua:103 -msgid "Digtron Digger" -msgstr "" - -#: digtron\nodes\node_digger.lua:128 -#: digtron\nodes\node_digger.lua:160 -msgid "Digtron Dual Digger" -msgstr "" - -#: digtron\nodes\node_misc.lua:7 -msgid "Digtron Structure" -msgstr "" - -#: digtron\nodes\node_misc.lua:41 -msgid "Digtron Light" -msgstr "" - -#: digtron\nodes\node_misc.lua:64 -msgid "Digtron Panel" -msgstr "" - -#: digtron\nodes\node_misc.lua:88 -msgid "Digtron Edge Panel" -msgstr "" - -#: digtron\nodes\node_misc.lua:117 -msgid "Digtron Corner Panel" -msgstr "" - -#: digtron\nodes\node_pipeworks_interface.lua:85 -msgid "Digtron Inventory Interface" -msgstr "" - -#: digtron\nodes\node_storage.lua:9 -#: digtron\nodes\node_storage.lua:213 -msgid "Inventory items" -msgstr "" - -#: digtron\nodes\node_storage.lua:20 -msgid "Digtron Inventory Storage" -msgstr "" - -#: digtron\nodes\node_storage.lua:102 -#: digtron\nodes\node_storage.lua:215 -msgid "Fuel items" -msgstr "" - -#: digtron\nodes\node_storage.lua:113 -msgid "Digtron Fuel Storage" -msgstr "" - -#: digtron\nodes\node_storage.lua:226 -msgid "Digtron Combined Storage" -msgstr "" - -#: digtron\nodes\recipes.lua:6 -msgid "Digtron Core" -msgstr "" diff --git a/locale/template.txt b/locale/template.txt new file mode 100644 index 0000000..bb72b47 --- /dev/null +++ b/locale/template.txt @@ -0,0 +1,149 @@ +# textdomain: digtron + + +### controller.lua ### + +@1 left= +Back= +Controls= +Cycles= +Delete= +Dig Move Build= +Dig Move Down= +Digtron Assembly= +Digtron Control Module= +Digtron name= +Disassemble= +Down= +Execute= +Forward= +Fuel= +Insert= +Left= +Main Inventory= +Move= +Move Back= +Move Down= +Move Forward= +Move Left= +Move Right= +Move Up= +New@nCommand= +Page @1/@2= +Pitch Down= +Pitch Up= +Reset= +Right= +Roll Clockwise= +Roll Widdershins= +Roll@nClockwise= +Roll@nWiddershins= +Rotate= +Sequence= +Up= +Yaw Left= +Yaw Right= + +### functions.lua ### + +@1 at @2 has encountered an obstacle.= +@1 at @2 requires @3 to execute its next build cycle.= +Copy of @1= +Unnamed Digtron= + +### node_builder.lua ### + +Builder for @1@nperiod @2, offset @3, extrusion @4= + +Builder will build once every n steps.@nThese steps are globally aligned, so all builders with the@nsame period and offset will build on the same location.= + +Builder will extrude this many blocks in the direction it is facing.@nCan be set from 1 to @1.@nNote that Digtron won't build into unloaded map regions.= + +Digtron Builder Module= +Extrusion= +Facing= + +Offsets the start of periodicity counting by this amount.@nFor example, a builder with period 2 and offset 0 builds@nevery even-numbered block and one with period 2 and@noffset 1 builds every odd-numbered block.= + +Read= + +Reads the facing of the block currently in the build location.= + +Saves settings, closes interface, and shows the locations this builder will build to in-world.= + +Value from 0-23. Not all block types make use of this.@nUse the 'Read & Save' button to copy the facing of the block@ncurrently in the builder output location.= + + +### node_builder.lua ### +### node_digger.lua ### + +Offset= +Periodicity= +Save &@nShow= + +### node_builder.lua ### +### node_digger.lua ### +### node_storage.lua ### + +This Digtron is active, interact with it via the controller node.= + + +### node_digger.lua ### + +Digger will dig once every n steps.@nThese steps are globally aligned, all diggers with@nthe same period and offset will dig on the same location.= + +Digger@nperiod @1, offset @2= +Digtron Digger= +Digtron Dual Digger= +Digtron Dual Soft Digger= +Digtron Soft Digger= + +Offsets the start of periodicity counting by this amount.@nFor example, a digger with period 2 and offset 0 digs@nevery even-numbered block and one with period 2 and@noffset 1 digs every odd-numbered block.= + +Saves settings= + +### node_duplicator.lua ### + +Amount of this component currently available.= + +Amount of this component required to copy the template Digtron.= + +Copy= +Digtron Duplicator= +Digtron component.= +Digtron components= + +Digtron components in this inventory will be used to create the duplicate.= + +Duplicate= +Duplication cannot proceed at this time.= +Place the Digtron you want to make a copy of here.= + +Puts a copy of the template Digtron into the output inventory slot.= + +Template= +The duplicate Digtron is output here.= + +### node_misc.lua ### + +Digtron Corner Panel= +Digtron Edge Panel= +Digtron Light= +Digtron Panel= +Digtron Structure= + +### node_pipeworks_interface.lua ### + +Digtron Inventory Interface= + +### node_storage.lua ### + +Digtron Combined Storage= +Digtron Fuel Storage= +Digtron Inventory Storage= +Fuel items= +Inventory items= + +### recipes.lua ### + +Digtron Core= diff --git a/locale/update.bat b/locale/update.bat deleted file mode 100644 index e87d44c..0000000 --- a/locale/update.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION -cd .. -set LIST= -for /r %%X in (*.lua) do set LIST=!LIST! %%X -..\intllib\tools\xgettext.bat %LIST% \ No newline at end of file diff --git a/mod.conf b/mod.conf index 1e351e6..6dd7671 100644 --- a/mod.conf +++ b/mod.conf @@ -6,4 +6,4 @@ license = MIT, LGPL 2.1 or later forum = https://forum.minetest.net/viewtopic.php?t=16295 version = 2.0 depends = default -optional_depends = intllib, creative \ No newline at end of file +optional_depends = creative, pipeworks \ No newline at end of file diff --git a/nodes/node_builder.lua b/nodes/node_builder.lua index ad25d74..95eb4d8 100644 --- a/nodes/node_builder.lua +++ b/nodes/node_builder.lua @@ -1,6 +1,4 @@ --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") +local S = digtron.S -- Note: builders go in group 4 diff --git a/nodes/node_digger.lua b/nodes/node_digger.lua index e986ab5..001ae53 100644 --- a/nodes/node_digger.lua +++ b/nodes/node_digger.lua @@ -1,6 +1,4 @@ --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") +local S = digtron.S -- TODO: make global, is used by builders too local player_interacting_with_digtron_pos = {} diff --git a/nodes/node_duplicator.lua b/nodes/node_duplicator.lua index 7034c24..97690cb 100644 --- a/nodes/node_duplicator.lua +++ b/nodes/node_duplicator.lua @@ -1,15 +1,4 @@ --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") - -local strip_digtron_prefix = function(desc) - -- Having "Digtron " prefixing every description is annoying - local prefix = "Digtron " - if desc:sub(1,#prefix) == prefix then - desc = desc:sub(#prefix+1) - end - return desc -end +local S = digtron.S local get_manifest = function(pos) local manifest = {} @@ -27,7 +16,7 @@ local get_manifest = function(pos) item = item_def._digtron_disassembled_node item_def = minetest.registered_items[item] end - local desc = strip_digtron_prefix(item_def.description) + local desc = item_def.description local entry = manifest[desc] if entry == nil then entry = {item = item} @@ -43,7 +32,7 @@ local get_manifest = function(pos) local main_list = inv:get_list("main") for _, itemstack in ipairs(main_list) do if not itemstack:is_empty() then - local desc = strip_digtron_prefix(itemstack:get_definition().description) + local desc = itemstack:get_definition().description local entry = manifest[desc] if entry == nil then entry = {item = item} diff --git a/nodes/node_misc.lua b/nodes/node_misc.lua index 9ae346d..3980e66 100644 --- a/nodes/node_misc.lua +++ b/nodes/node_misc.lua @@ -1,6 +1,4 @@ --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") +local S = digtron.S -- A do-nothing "structural" node, to ensure all digtron nodes that are supposed to be connected to each other can be connected to each other. minetest.register_node("digtron:structure", { diff --git a/nodes/node_pipeworks_interface.lua b/nodes/node_pipeworks_interface.lua new file mode 100644 index 0000000..9dfc4ef --- /dev/null +++ b/nodes/node_pipeworks_interface.lua @@ -0,0 +1,134 @@ +local S = digtron.S + +--Build up the formspec, somewhat complicated due to multiple mod options +local pipeworks_path = minetest.get_modpath("pipeworks") + + +local function eject_items(pos, node, player, eject_even_without_pipeworks, layout) + local dir = minetest.facedir_to_dir(node.param2) + local destination_pos = vector.add(pos, dir) + local destination_node_name = minetest.get_node(destination_pos).name + local destination_node_def = minetest.registered_nodes[destination_node_name] + + local insert_into_pipe = false + + -- Build a list of all the items that builder nodes want to use. + local filter_items = {} + if layout.builders ~= nil then + for _, node_image in pairs(layout.builders) do + filter_items[node_image.meta.inventory.main[1]:get_name()] = true + end + end + + -- Look through the inventories and find an item that's not on that list. + local source_node = nil + local source_index = nil + local source_stack = nil + for _, node_image in pairs(layout.inventories or {}) do + if type(node_image.meta.inventory.main) ~= "table" then + node_image.meta.inventory.main = {} + end + for index, item_stack in pairs(node_image.meta.inventory.main) do + if item_stack:get_count() > 0 and not filter_items[item_stack:get_name()] then + source_node = node_image + source_index = index + source_stack = item_stack + node_image.meta.inventory.main[index] = nil + break + end + end + if source_node then break end + end + + if source_node then + local meta = minetest.get_meta(source_node.pos) + local inv = meta:get_inventory() + + if insert_into_pipe then + local from_pos = vector.add(pos, vector.multiply(dir, 0.5)) + local start_pos = pos + inv:set_stack("main", source_index, nil) + pipeworks.tube_inject_item(from_pos, start_pos, dir, source_stack, player:get_player_name()) + minetest.sound_play("steam_puff", {gain=0.5, pos=pos}) + return true + end + end + + -- couldn't find an item to eject + return false +end + + +-- TODO: hoppers need enhancement to support this + +---- Hopper compatibility +--if minetest.get_modpath("hopper") and hopper ~= nil and hopper.add_container ~= nil then +-- hopper:add_container({ +-- {"top", "digtron:inventory", "main"}, +-- {"bottom", "digtron:inventory", "main"}, +-- {"side", "digtron:inventory", "main"}, +-- +-- {"top", "digtron:fuelstore", "fuel"}, +-- {"bottom", "digtron:fuelstore", "fuel"}, +-- {"side", "digtron:fuelstore", "fuel"}, +-- +-- {"top", "digtron:combined_storage", "main"}, +-- {"bottom", "digtron:combined_storage", "main"}, +-- {"side", "digtron:combined_storage", "fuel"}, +-- }) +--end + + +local ejector_def = { + description = S("Digtron Inventory Interface"), + _doc_items_longdesc = digtron.doc.inventory_ejector_longdesc, + _doc_items_usagehelp = digtron.doc.inventory_ejector_usagehelp, + _digtron_formspec = ejector_formspec, + groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 9, tubedevice = 1}, + tiles = {"digtron_plate.png", "digtron_plate.png", "digtron_plate.png", "digtron_plate.png", "digtron_plate.png^digtron_output.png", "digtron_plate.png^digtron_output_back.png"}, + drawtype = "nodebox", + sounds = digtron.metal_sounds, + paramtype = "light", + paramtype2 = "facedir", + is_ground_content = false, + node_box = { + type = "fixed", + fixed = { + {-0.5, -0.5, -0.5, 0.5, 0.5, 0.1875}, -- NodeBox1 + {-0.3125, -0.3125, 0.1875, 0.3125, 0.3125, 0.3125}, -- NodeBox2 + {-0.1875, -0.1875, 0.3125, 0.1875, 0.1875, 0.5}, -- NodeBox3 + } + }, + + tube = (function() if pipeworks_path then return { + insert_object = function(pos, node, stack, direction) + local meta = minetest.get_meta(pos) + local digtron_id = meta:get_string("digtron_id") + local inv = digtron.retrieve_inventory(digtron_id) + if inv == nil then + -- TODO error message + return + end + +-- return inv:add_item("main", stack) + end, + can_insert = function(pos, node, stack, direction) + local meta = minetest.get_meta(pos) + local digtron_id = meta:get_string("digtron_id") + local inv = digtron.retrieve_inventory(digtron_id) + if inv == nil then + -- TODO error message + return + end + +-- return inv:room_for_item("main", stack) + end, + input_inventory = "main", + connect_sides = {back = 1} + } end end)(), + + after_place_node = (function() if pipeworks_path then return pipeworks.after_place end end)(), + after_dig_node = (function() if pipeworks_path then return pipeworks.after_dig end end)() +}) + +minetest.register_node("digtron:pipeworks_interface", ejector_def) diff --git a/nodes/node_storage.lua b/nodes/node_storage.lua index 1bd5dae..a0b4ffe 100644 --- a/nodes/node_storage.lua +++ b/nodes/node_storage.lua @@ -1,6 +1,4 @@ --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") +local S = digtron.S --local pipeworks_path = minetest.get_modpath("pipeworks") diff --git a/nodes/recipes.lua b/nodes/recipes.lua index 1ff70a3..d876c7a 100644 --- a/nodes/recipes.lua +++ b/nodes/recipes.lua @@ -1,6 +1,4 @@ --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") +local S = digtron.S minetest.register_craftitem("digtron:digtron_core", { description = S("Digtron Core"),