Improve TA3 terminal, adapt manuals
This commit is contained in:
parent
9ab69c10e1
commit
270f97a5d8
28
COPYING.txt
28
COPYING.txt
@ -1,28 +0,0 @@
|
|||||||
The Techage mod for Minetest is
|
|
||||||
|
|
||||||
Copyright (C) 2019-2020 Joachim Stolberg
|
|
||||||
|
|
||||||
License of source code
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
This mod is free software; you can redistribute and/or
|
|
||||||
modify it under the terms of the GNU General Public License version 3 or later
|
|
||||||
published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This mod is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public
|
|
||||||
License along with this mod; if not, write to the
|
|
||||||
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
||||||
Boston, MA 02110-1301, USA.
|
|
||||||
|
|
||||||
|
|
||||||
License of media (textures, sounds and documentation)
|
|
||||||
-----------------------------------------------------
|
|
||||||
|
|
||||||
All textures, sounds and documentation files are licensed under the
|
|
||||||
Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
|
|
||||||
http://creativecommons.org/licenses/by-sa/3.0/
|
|
@ -84,6 +84,7 @@ minetest.register_entity(":techage:region_cube", {
|
|||||||
textures = {"techage_cube_mark.png"},
|
textures = {"techage_cube_mark.png"},
|
||||||
use_texture_alpha = true,
|
use_texture_alpha = true,
|
||||||
physical = false,
|
physical = false,
|
||||||
|
glow = 12,
|
||||||
},
|
},
|
||||||
on_step = function(self, dtime)
|
on_step = function(self, dtime)
|
||||||
if marker_region[self.player_name] == nil then
|
if marker_region[self.player_name] == nil then
|
||||||
|
@ -114,7 +114,7 @@ minetest.register_entity(":techage:position_side", {
|
|||||||
physical = false,
|
physical = false,
|
||||||
visual_size = {x = 1.1, y = 1.1, z = 1.1},
|
visual_size = {x = 1.1, y = 1.1, z = 1.1},
|
||||||
collisionbox = {-0.55,-0.55,-0.55, 0.55,0.55,0.55},
|
collisionbox = {-0.55,-0.55,-0.55, 0.55,0.55,0.55},
|
||||||
glow = 8,
|
glow = 12,
|
||||||
},
|
},
|
||||||
on_step = function(self, dtime)
|
on_step = function(self, dtime)
|
||||||
if marker_region[self.player_name] == nil then
|
if marker_region[self.player_name] == nil then
|
||||||
|
@ -435,6 +435,8 @@ techage.manual_DE.aText = {
|
|||||||
"\n"..
|
"\n"..
|
||||||
"Der Verteiler besitzt dazu ein Menü mit 4 Filter mit unterschiedlichen Farben\\, entsprechend den 4 Ausgängen. Soll ein Ausgang genutzt werden\\, so muss der entsprechende Filter über die \"on\" Checkbox aktiviert werden. Alle Items\\, die für diesen Filter konfiguriert sind\\, werden über den zugeordneten Ausgang ausgegeben. Wird ein Filter aktiviert\\, ohne das Items konfiguriert werden\\, so sprechen wir hier von einem \"nicht-konfigurierten\"\\, offenen Ausgang.\n"..
|
"Der Verteiler besitzt dazu ein Menü mit 4 Filter mit unterschiedlichen Farben\\, entsprechend den 4 Ausgängen. Soll ein Ausgang genutzt werden\\, so muss der entsprechende Filter über die \"on\" Checkbox aktiviert werden. Alle Items\\, die für diesen Filter konfiguriert sind\\, werden über den zugeordneten Ausgang ausgegeben. Wird ein Filter aktiviert\\, ohne das Items konfiguriert werden\\, so sprechen wir hier von einem \"nicht-konfigurierten\"\\, offenen Ausgang.\n"..
|
||||||
"\n"..
|
"\n"..
|
||||||
|
"*Achtung: Der Verteiler ist an seinen Ausgängen gleichzeitig ein Schieber. Daher niemals die Gegenstände mit einem Schieber aus dem Verteiler ziehen!*\n"..
|
||||||
|
"\n"..
|
||||||
"Für einen nicht-konfigurierten Ausgang gibt es zwei Betriebsarten:\n"..
|
"Für einen nicht-konfigurierten Ausgang gibt es zwei Betriebsarten:\n"..
|
||||||
"\n"..
|
"\n"..
|
||||||
"1) Alle Items ausgeben\\, die an keine anderen Ausgängen ausgegeben werden können\\, auch wenn diese blockiert sind.\n"..
|
"1) Alle Items ausgeben\\, die an keine anderen Ausgängen ausgegeben werden können\\, auch wenn diese blockiert sind.\n"..
|
||||||
@ -961,7 +963,7 @@ techage.manual_DE.aText = {
|
|||||||
" - 'pub' schalte in den öffentlichen Modus um\n"..
|
" - 'pub' schalte in den öffentlichen Modus um\n"..
|
||||||
" - 'priv' schalte in den privaten Modus um\n"..
|
" - 'priv' schalte in den privaten Modus um\n"..
|
||||||
"\n"..
|
"\n"..
|
||||||
"Im privaten Modul kann nur der Besitzer selbst Kommandos eingeben oder Tasten nutzen.\n"..
|
"Im privaten Modus (private) kann das Terminal nur von Spielern verwendet werden\\, die an diesem Ort bauen können\\, also Protection Rechte besitzen. Im öffentlichen Modus (public) können alle Spieler die vorkonfigurierten Tasten verwenden.\n"..
|
||||||
"\n"..
|
"\n"..
|
||||||
"\n"..
|
"\n"..
|
||||||
"\n",
|
"\n",
|
||||||
|
@ -434,6 +434,8 @@ techage.manual_EN.aText = {
|
|||||||
"\n"..
|
"\n"..
|
||||||
"The distributor has a menu with 4 filters with different colors\\, corresponding to the 4 outputs. If an output is to be used\\, the corresponding filter must be activated via the \"on\" checkbox. All items that are configured for this filter are output via the assigned output. If a filter is activated without items being configured\\, we are talking about an \"unconfigured\"\\, open output.\n"..
|
"The distributor has a menu with 4 filters with different colors\\, corresponding to the 4 outputs. If an output is to be used\\, the corresponding filter must be activated via the \"on\" checkbox. All items that are configured for this filter are output via the assigned output. If a filter is activated without items being configured\\, we are talking about an \"unconfigured\"\\, open output.\n"..
|
||||||
"\n"..
|
"\n"..
|
||||||
|
"*Attention: The distributor is also a pusher at its output sides. Therefore\\, never pull items out of the distributor with a pusher!*\n"..
|
||||||
|
"\n"..
|
||||||
"There are two operating modes for a non-configured output:\n"..
|
"There are two operating modes for a non-configured output:\n"..
|
||||||
"\n"..
|
"\n"..
|
||||||
"1) Output all items that cannot be output to any other exit\\, even if they are blocked.\n"..
|
"1) Output all items that cannot be output to any other exit\\, even if they are blocked.\n"..
|
||||||
@ -950,7 +952,9 @@ techage.manual_EN.aText = {
|
|||||||
" - 'pub' switch to public mode\n"..
|
" - 'pub' switch to public mode\n"..
|
||||||
" - 'priv' switch to private mode\n"..
|
" - 'priv' switch to private mode\n"..
|
||||||
"\n"..
|
"\n"..
|
||||||
"In the private mode\\, only the owner can enter commands himself or use keys.\n"..
|
"In private mode\\, the terminal can only be used by players who can build at this location\\, i.e. who have protection rights.\n"..
|
||||||
|
"\n"..
|
||||||
|
"In public mode\\, all players can use the preconfigured keys.\n"..
|
||||||
"\n"..
|
"\n"..
|
||||||
"\n"..
|
"\n"..
|
||||||
"\n",
|
"\n",
|
||||||
|
@ -185,7 +185,7 @@ local PN270 = {"techage_gaspipe_knee.png^[transformR270", "techage:ta4_pipeS"}
|
|||||||
techage.ConstructionPlans["ta3_tank"] = {
|
techage.ConstructionPlans["ta3_tank"] = {
|
||||||
{false, false, false, false, false, false, false, false, false, false},
|
{false, false, false, false, false, false, false, false, false, false},
|
||||||
{false, Tubes, PushR, Tubes, Fillr, Tubes, PushR, Tubes, false, false},
|
{false, Tubes, PushR, Tubes, Fillr, Tubes, PushR, Tubes, false, false},
|
||||||
{false, false, false, false, TANK3, PIPEH, PIPEH, Pump, PIPEH, Tank},
|
{false, false, false, false, TANK3, PIPEH, PIPEH, Pump, PIPEH, false},
|
||||||
{false, false, false, false, false, false, false, false, false, false},
|
{false, false, false, false, false, false, false, false, false, false},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
469
i18n.py
469
i18n.py
@ -1,79 +1,448 @@
|
|||||||
#!/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.
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019 Joachim Stolberg
|
# Copyright (C) 2019 Joachim Stolberg, 2020 FaceDeer, 2020 Louis Royer
|
||||||
# LGPLv2.1+
|
# LGPLv2.1+
|
||||||
#
|
#
|
||||||
# Copy the script into the mod root folder and adapt the last code lines to you needs.
|
# See https://github.com/minetest-tools/update_translations for
|
||||||
|
# potential future updates to this script.
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
import os, fnmatch, re, shutil
|
import os, fnmatch, re, shutil, errno
|
||||||
|
from sys import argv as _argv
|
||||||
|
from sys import stderr as _stderr
|
||||||
|
|
||||||
pattern_lua = re.compile(r'[ \.=^\t]S\("(.+?)"\)', re.DOTALL)
|
# Running params
|
||||||
pattern_tr = re.compile(r'(.+?[^@])=(.+)')
|
params = {"recursive": False,
|
||||||
|
"help": False,
|
||||||
|
"mods": False,
|
||||||
|
"verbose": False,
|
||||||
|
"folders": [],
|
||||||
|
"no-old-file": False
|
||||||
|
}
|
||||||
|
# Available CLI options
|
||||||
|
options = {"recursive": ['--recursive', '-r'],
|
||||||
|
"help": ['--help', '-h'],
|
||||||
|
"mods": ['--installed-mods'],
|
||||||
|
"verbose": ['--verbose', '-v'],
|
||||||
|
"no-old-file": ['--no-old-file']
|
||||||
|
}
|
||||||
|
|
||||||
def gen_template(templ_file, lkeyStrings):
|
# Strings longer than this will have extra space added between
|
||||||
lOut = []
|
# them in the translation files to make it easier to distinguish their
|
||||||
lkeyStrings.sort()
|
# beginnings and endings at a glance
|
||||||
for s in lkeyStrings:
|
doublespace_threshold = 60
|
||||||
lOut.append("%s=" % s)
|
|
||||||
open(templ_file, "wt").write("\n".join(lOut))
|
|
||||||
|
|
||||||
|
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["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])
|
||||||
|
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, 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):
|
def read_lua_file_strings(lua_file):
|
||||||
lOut = []
|
lOut = []
|
||||||
text = open(lua_file).read()
|
with open(lua_file, encoding='utf-8') as text_file:
|
||||||
for s in pattern_lua.findall(text):
|
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(r'"\.\.\s+"', "", s)
|
||||||
s = re.sub("@[^@=n]", "@@", 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("\\n", "@n")
|
s = s.replace("\\n", "@n")
|
||||||
s = s.replace("=", "@=")
|
s = s.replace("=", "@=")
|
||||||
lOut.append(s)
|
lOut.append(s)
|
||||||
return lOut
|
return lOut
|
||||||
|
|
||||||
def inport_tr_file(tr_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.
|
||||||
|
# Returns also header comments in the third return value.
|
||||||
|
def import_tr_file(tr_file):
|
||||||
dOut = {}
|
dOut = {}
|
||||||
|
text = None
|
||||||
|
header_comment = None
|
||||||
if os.path.exists(tr_file):
|
if os.path.exists(tr_file):
|
||||||
for line in open(tr_file, "r").readlines():
|
with open(tr_file, "r", encoding='utf-8') as existing_file :
|
||||||
s = line.strip()
|
# save the full text to allow for comparison
|
||||||
if s == "" or s[0] == "#":
|
# of the old version with the new output
|
||||||
continue
|
text = existing_file.read()
|
||||||
match = pattern_tr.match(s)
|
existing_file.seek(0)
|
||||||
if match:
|
# a running record of the current comment block
|
||||||
dOut[match.group(1)] = match.group(2)
|
# we're inside, to allow preceeding multi-line comments
|
||||||
return dOut
|
# 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 or ""
|
||||||
|
# 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
|
||||||
|
|
||||||
def generate_template(templ_file):
|
# Reset comment block if we hit a header
|
||||||
lOut = []
|
latest_comment_block = None
|
||||||
for root, dirs, files in os.walk('./'):
|
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:
|
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, len(found))
|
if params["verbose"]:
|
||||||
lOut.extend(found)
|
print(f"{fname}: {str(len(found))} translatable strings")
|
||||||
lOut = list(set(lOut))
|
|
||||||
lOut.sort()
|
|
||||||
gen_template(templ_file, lOut)
|
|
||||||
return lOut
|
|
||||||
|
|
||||||
def update_tr_file(lNew, mod_name, tr_file):
|
for s in found:
|
||||||
lOut = ["# textdomain: %s\n" % mod_name]
|
sources = dOut.get(s, set())
|
||||||
if os.path.exists(tr_file):
|
sources.add(f"### {os.path.basename(fname)} ###")
|
||||||
shutil.copyfile(tr_file, tr_file+".old")
|
dOut[s] = sources
|
||||||
dOld = inport_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]))
|
|
||||||
open(tr_file, "w").write("\n".join(lOut))
|
|
||||||
|
|
||||||
data = generate_template("./locale/template.txt")
|
if len(dOut) == 0:
|
||||||
update_tr_file(data, "techage", "./locale/techage.de.tr")
|
return None
|
||||||
#update_tr_file(data, "techage", "./locale/techage.fr.tr")
|
templ_file = os.path.join(folder, "locale/template.txt")
|
||||||
print("Done.\n")
|
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 + "/")
|
||||||
|
|
||||||
|
|
||||||
|
main()
|
||||||
|
1320
locale/techage.de.tr
1320
locale/techage.de.tr
File diff suppressed because it is too large
Load Diff
1316
locale/template.txt
1316
locale/template.txt
File diff suppressed because it is too large
Load Diff
@ -15,41 +15,23 @@
|
|||||||
local M = minetest.get_meta
|
local M = minetest.get_meta
|
||||||
local S = techage.S
|
local S = techage.S
|
||||||
|
|
||||||
local HELP_TA3 = S("#### TA3 Terminal ####@n"..
|
local HELP_TA3 = "Syntax:\n"..
|
||||||
"@n"..
|
" cmd <num> <cmnd>\n"..
|
||||||
"Send commands to your machines@n"..
|
"\n"..
|
||||||
"and output text messages from your@n"..
|
"like: cmd 181 on\n"..
|
||||||
"machines to the Terminal.@n"..
|
"or: cmd 4573 state\n"..
|
||||||
"@n"..
|
"\n"..
|
||||||
"Command syntax:@n"..
|
"Local commands:\n"..
|
||||||
" cmd <num> <cmnd>@n"..
|
"- clear = clear screen\n"..
|
||||||
"@n"..
|
"- help = this message\n"..
|
||||||
"example: cmd 181 on@n"..
|
"- pub = switch to public use of buttons\n"..
|
||||||
"<num> is the number of the node to which the command is sent@n"..
|
"- priv = switch to private use of buttons\n"..
|
||||||
"'on' is the command to turn machines/nodes on@n"..
|
"To program a user button with a command:\n"..
|
||||||
"Further commands can be retrieved by clicking on@n"..
|
" set <button-num> <button-text> <command>\n"..
|
||||||
"machines/nodes with the Techage Info Tool.@n"..
|
"e.g.: set 1 ON cmd 123 on"
|
||||||
"@n"..
|
|
||||||
"Local commands:@n"..
|
|
||||||
"- clear = clear screen@n"..
|
|
||||||
"- help = this message@n"..
|
|
||||||
"- pub = switch to public use@n"..
|
|
||||||
"- priv = switch to private use@n"..
|
|
||||||
"To program a user button with a command:@n"..
|
|
||||||
" set <button-num> <button-text> <command>@n"..
|
|
||||||
"e.g. 'set 1 ON cmd 123 on'@n")
|
|
||||||
|
|
||||||
local CMNDS_TA3 = S("Syntax error, try help")
|
local CMNDS_TA3 = S("Syntax error, try help")
|
||||||
|
|
||||||
--local function formspec1()
|
|
||||||
-- return "size[6,4]"..
|
|
||||||
-- default.gui_bg..
|
|
||||||
-- default.gui_bg_img..
|
|
||||||
-- default.gui_slots..
|
|
||||||
-- "field[0.5,1;5,1;number;Techage Controller number:;]" ..
|
|
||||||
-- "button_exit[1.5,2.5;2,1;exit;Save]"
|
|
||||||
--end
|
|
||||||
|
|
||||||
local function get_string(meta, num, default)
|
local function get_string(meta, num, default)
|
||||||
local s = meta:get_string("bttn_text"..num)
|
local s = meta:get_string("bttn_text"..num)
|
||||||
if not s or s == "" then
|
if not s or s == "" then
|
||||||
@ -106,6 +88,7 @@ end
|
|||||||
local function command(pos, command, player)
|
local function command(pos, command, player)
|
||||||
local meta = minetest.get_meta(pos)
|
local meta = minetest.get_meta(pos)
|
||||||
local owner = meta:get_string("owner") or ""
|
local owner = meta:get_string("owner") or ""
|
||||||
|
|
||||||
if command then
|
if command then
|
||||||
command = command:sub(1,80)
|
command = command:sub(1,80)
|
||||||
command = string.trim(command)
|
command = string.trim(command)
|
||||||
@ -117,16 +100,15 @@ local function command(pos, command, player)
|
|||||||
local meta = minetest.get_meta(pos)
|
local meta = minetest.get_meta(pos)
|
||||||
meta:set_string("output", HELP_TA3)
|
meta:set_string("output", HELP_TA3)
|
||||||
meta:set_string("formspec", formspec2(meta))
|
meta:set_string("formspec", formspec2(meta))
|
||||||
elseif command == "pub" and owner == player then
|
elseif command == "pub" then
|
||||||
meta:set_int("public", 1)
|
meta:set_int("public", 1)
|
||||||
output(pos, player..":$ "..command)
|
output(pos, player..":$ "..command)
|
||||||
output(pos, S("Switched to public use!"))
|
output(pos, S("Switched to public use!"))
|
||||||
elseif command == "priv" and owner == player then
|
elseif command == "priv" then
|
||||||
meta:set_int("public", 0)
|
meta:set_int("public", 0)
|
||||||
output(pos, player..":$ "..command)
|
output(pos, player..":$ "..command)
|
||||||
output(pos, S("Switched to private use!"))
|
output(pos, S("Switched to private use!"))
|
||||||
elseif meta:get_int("public") == 1 or owner == player or
|
else
|
||||||
minetest.check_player_privs(player, "server") then
|
|
||||||
output(pos, "$ "..command)
|
output(pos, "$ "..command)
|
||||||
local own_num = meta:get_string("node_number")
|
local own_num = meta:get_string("node_number")
|
||||||
local num, cmnd, payload = command:match('^cmd%s+([0-9]+)%s+(%w+)%s*(.*)$')
|
local num, cmnd, payload = command:match('^cmd%s+([0-9]+)%s+(%w+)%s*(.*)$')
|
||||||
@ -206,16 +188,25 @@ local function register_terminal(num, tiles, node_box, selection_box)
|
|||||||
|
|
||||||
on_receive_fields = function(pos, formname, fields, player)
|
on_receive_fields = function(pos, formname, fields, player)
|
||||||
local meta = minetest.get_meta(pos)
|
local meta = minetest.get_meta(pos)
|
||||||
|
local public = meta:get_int("public") == 1
|
||||||
|
local protected = minetest.is_protected(pos, player:get_player_name())
|
||||||
|
|
||||||
|
if not protected then
|
||||||
local evt = minetest.explode_table_event(fields.output)
|
local evt = minetest.explode_table_event(fields.output)
|
||||||
if evt.type == "DCL" then
|
if evt.type == "DCL" then
|
||||||
local s = get_line_text(pos, evt.row)
|
local s = get_line_text(pos, evt.row)
|
||||||
meta:set_string("command", s)
|
meta:set_string("command", s)
|
||||||
meta:set_string("formspec", formspec2(meta))
|
meta:set_string("formspec", formspec2(meta))
|
||||||
|
return
|
||||||
elseif (fields.key_enter == "true" or fields.ok) and fields.cmnd ~= "" then
|
elseif (fields.key_enter == "true" or fields.ok) and fields.cmnd ~= "" then
|
||||||
command(pos, fields.cmnd, player:get_player_name())
|
command(pos, fields.cmnd, player:get_player_name())
|
||||||
meta:set_string("command", "")
|
meta:set_string("command", "")
|
||||||
meta:set_string("formspec", formspec2(meta))
|
meta:set_string("formspec", formspec2(meta))
|
||||||
elseif fields.bttn1 then send_cmnd(pos, meta, 1)
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if public or not protected then
|
||||||
|
if fields.bttn1 then send_cmnd(pos, meta, 1)
|
||||||
elseif fields.bttn2 then send_cmnd(pos, meta, 2)
|
elseif fields.bttn2 then send_cmnd(pos, meta, 2)
|
||||||
elseif fields.bttn3 then send_cmnd(pos, meta, 3)
|
elseif fields.bttn3 then send_cmnd(pos, meta, 3)
|
||||||
elseif fields.bttn4 then send_cmnd(pos, meta, 4)
|
elseif fields.bttn4 then send_cmnd(pos, meta, 4)
|
||||||
@ -225,6 +216,7 @@ local function register_terminal(num, tiles, node_box, selection_box)
|
|||||||
elseif fields.bttn8 then send_cmnd(pos, meta, 8)
|
elseif fields.bttn8 then send_cmnd(pos, meta, 8)
|
||||||
elseif fields.bttn9 then send_cmnd(pos, meta, 9)
|
elseif fields.bttn9 then send_cmnd(pos, meta, 9)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
after_dig_node = function(pos, oldnode, oldmetadata)
|
after_dig_node = function(pos, oldnode, oldmetadata)
|
||||||
|
@ -590,7 +590,7 @@ Das Terminal besitzt folgende, lokalen Kommandos:
|
|||||||
- `pub` schalte in den öffentlichen Modus um
|
- `pub` schalte in den öffentlichen Modus um
|
||||||
- `priv` schalte in den privaten Modus um
|
- `priv` schalte in den privaten Modus um
|
||||||
|
|
||||||
Im privaten Modul kann nur der Besitzer selbst Kommandos eingeben oder Tasten nutzen.
|
Im privaten Modus (private) kann das Terminal nur von Spielern verwendet werden, die an diesem Ort bauen können, also Protection Rechte besitzen. Im öffentlichen Modus (public) können alle Spieler die vorkonfigurierten Tasten verwenden.
|
||||||
|
|
||||||
[ta3_terminal|image]
|
[ta3_terminal|image]
|
||||||
|
|
||||||
|
@ -579,7 +579,9 @@ The terminal has the following local commands:
|
|||||||
- `pub` switch to public mode
|
- `pub` switch to public mode
|
||||||
- `priv` switch to private mode
|
- `priv` switch to private mode
|
||||||
|
|
||||||
In the private mode, only the owner can enter commands himself or use keys.
|
In private mode, the terminal can only be used by players who can build at this location, i.e. who have protection rights.
|
||||||
|
|
||||||
|
In public mode, all players can use the preconfigured keys.
|
||||||
|
|
||||||
[ta3_terminal|image]
|
[ta3_terminal|image]
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user