forked from mtcontrib/x_enchanting
Add translations
This commit is contained in:
parent
4b795b7ed3
commit
d27a607543
@ -21,7 +21,8 @@
|
|||||||
"/docs",
|
"/docs",
|
||||||
"/bin"
|
"/bin"
|
||||||
],
|
],
|
||||||
"checkThirdParty": false
|
"checkThirdParty": false,
|
||||||
|
"useGitIgnore": true
|
||||||
},
|
},
|
||||||
"typeFormat": {
|
"typeFormat": {
|
||||||
"config": {
|
"config": {
|
||||||
@ -55,7 +56,9 @@
|
|||||||
"ignoredFiles": "Disable",
|
"ignoredFiles": "Disable",
|
||||||
"libraryFiles": "Disable",
|
"libraryFiles": "Disable",
|
||||||
"neededFileStatus": {
|
"neededFileStatus": {
|
||||||
"codestyle-check": "Any"
|
"codestyle-check": "Opened",
|
||||||
|
"param-type-mismatch": "Opened",
|
||||||
|
"assign-type-mismatch": "Opened"
|
||||||
},
|
},
|
||||||
"disableScheme": [
|
"disableScheme": [
|
||||||
"git",
|
"git",
|
||||||
|
27
api.lua
27
api.lua
@ -1,5 +1,7 @@
|
|||||||
default = minetest.global_exists('default') and default --[[@as MtgDefault]]
|
default = minetest.global_exists('default') and default --[[@as MtgDefault]]
|
||||||
|
|
||||||
|
local S = minetest.get_translator(minetest.get_current_modname())
|
||||||
|
|
||||||
---@type XEnchanting
|
---@type XEnchanting
|
||||||
XEnchanting = {
|
XEnchanting = {
|
||||||
tools_enchantability = {
|
tools_enchantability = {
|
||||||
@ -50,7 +52,7 @@ XEnchanting = {
|
|||||||
-- Living things like animals and the player. This could imply
|
-- Living things like animals and the player. This could imply
|
||||||
-- some blood effects when hitting
|
-- some blood effects when hitting
|
||||||
sharpness = {
|
sharpness = {
|
||||||
name = 'Sharpness',
|
name = S('Sharpness'),
|
||||||
-- what level should be taken, `level = min/max values`
|
-- what level should be taken, `level = min/max values`
|
||||||
final_level_range = {
|
final_level_range = {
|
||||||
[1] = { 1, 21 },
|
[1] = { 1, 21 },
|
||||||
@ -70,7 +72,7 @@ XEnchanting = {
|
|||||||
weight = 10
|
weight = 10
|
||||||
},
|
},
|
||||||
fortune = {
|
fortune = {
|
||||||
name = 'Fortune',
|
name = S('Fortune'),
|
||||||
-- what level should be taken, `level = min/max values`
|
-- what level should be taken, `level = min/max values`
|
||||||
final_level_range = {
|
final_level_range = {
|
||||||
[1] = { 15, 65 },
|
[1] = { 15, 65 },
|
||||||
@ -86,7 +88,7 @@ XEnchanting = {
|
|||||||
weight = 2
|
weight = 2
|
||||||
},
|
},
|
||||||
unbreaking = {
|
unbreaking = {
|
||||||
name = 'Unbreaking',
|
name = S('Unbreaking'),
|
||||||
-- what level should be taken, `level = min/max values`
|
-- what level should be taken, `level = min/max values`
|
||||||
final_level_range = {
|
final_level_range = {
|
||||||
[1] = { 5, 55 },
|
[1] = { 5, 55 },
|
||||||
@ -102,7 +104,7 @@ XEnchanting = {
|
|||||||
weight = 5
|
weight = 5
|
||||||
},
|
},
|
||||||
efficiency = {
|
efficiency = {
|
||||||
name = 'Efficiency',
|
name = S('Efficiency'),
|
||||||
-- what level should be taken, `level = min/max values`
|
-- what level should be taken, `level = min/max values`
|
||||||
final_level_range = {
|
final_level_range = {
|
||||||
[1] = { 1, 51 },
|
[1] = { 1, 51 },
|
||||||
@ -330,7 +332,7 @@ function XEnchanting.get_enchanted_tool_capabilities(self, tool_def, enchantment
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
enchantments_desc = '\n' .. minetest.colorize('#AE81FF', 'Enchanted')
|
enchantments_desc = '\n' .. minetest.colorize('#AE81FF', S('Enchanted'))
|
||||||
.. '\n' .. table.concat(enchantments_desc, '\n')
|
.. '\n' .. table.concat(enchantments_desc, '\n')
|
||||||
enchantments_desc_masked = table.concat(enchantments_desc_masked, '') .. '...?'
|
enchantments_desc_masked = table.concat(enchantments_desc_masked, '') .. '...?'
|
||||||
|
|
||||||
@ -353,7 +355,7 @@ function XEnchanting.set_enchanted_tool(self, pos, itemstack, capabilities, desc
|
|||||||
|
|
||||||
stack_meta:set_tool_capabilities(capabilities)
|
stack_meta:set_tool_capabilities(capabilities)
|
||||||
stack_meta:set_string('description', itemstack:get_description() .. '\n' .. description)
|
stack_meta:set_string('description', itemstack:get_description() .. '\n' .. description)
|
||||||
stack_meta:set_string('short_description', 'Enchanted ' .. itemstack:get_short_description())
|
stack_meta:set_string('short_description', S('Enchanted') .. ' ' .. itemstack:get_short_description())
|
||||||
stack_meta:set_int('is_enchanted', 1)
|
stack_meta:set_int('is_enchanted', 1)
|
||||||
|
|
||||||
inv:set_stack('item', 1, itemstack)
|
inv:set_stack('item', 1, itemstack)
|
||||||
@ -423,7 +425,6 @@ function XEnchanting.get_enchantment_data(self, nr_of_bookshelfs, tool_def)
|
|||||||
-- 2 Find possible enchantments
|
-- 2 Find possible enchantments
|
||||||
----
|
----
|
||||||
|
|
||||||
---@type {["id"]: string, ["value"]: number | table, ["level"]: number}[]
|
|
||||||
local possible_enchantments = {}
|
local possible_enchantments = {}
|
||||||
|
|
||||||
-- Get level
|
-- Get level
|
||||||
@ -456,7 +457,6 @@ function XEnchanting.get_enchantment_data(self, nr_of_bookshelfs, tool_def)
|
|||||||
-- 3 Select a set of enchantments from the list
|
-- 3 Select a set of enchantments from the list
|
||||||
----
|
----
|
||||||
|
|
||||||
---@type {["id"]: string, ["value"]: number | table, ["level"]: number}[]
|
|
||||||
local final_enchantments = {}
|
local final_enchantments = {}
|
||||||
local total_weight = 0
|
local total_weight = 0
|
||||||
|
|
||||||
@ -496,11 +496,6 @@ function XEnchanting.get_enchantment_data(self, nr_of_bookshelfs, tool_def)
|
|||||||
return data
|
return data
|
||||||
end
|
end
|
||||||
|
|
||||||
---Build form
|
|
||||||
---@param pos Vector
|
|
||||||
---@param player_name string
|
|
||||||
---@param data? table
|
|
||||||
---@return string
|
|
||||||
function XEnchanting.get_formspec(self, pos, player_name, data)
|
function XEnchanting.get_formspec(self, pos, player_name, data)
|
||||||
local spos = pos.x .. ',' .. pos.y .. ',' .. pos.z
|
local spos = pos.x .. ',' .. pos.y .. ',' .. pos.z
|
||||||
local inv = minetest.get_meta(pos):get_inventory()
|
local inv = minetest.get_meta(pos):get_inventory()
|
||||||
@ -512,7 +507,7 @@ function XEnchanting.get_formspec(self, pos, player_name, data)
|
|||||||
|
|
||||||
local formspec = {
|
local formspec = {
|
||||||
'size[8,9]',
|
'size[8,9]',
|
||||||
'label[0, 0;Enchant]',
|
'label[0, 0;' .. S('Enchant') .. ']',
|
||||||
'list[nodemeta:' .. spos .. ';item;0, 2.5;1, 1;]',
|
'list[nodemeta:' .. spos .. ';item;0, 2.5;1, 1;]',
|
||||||
'image[1,2.5;1,1;x_enchanting_trade_slot.png;]',
|
'image[1,2.5;1,1;x_enchanting_trade_slot.png;]',
|
||||||
'list[nodemeta:' .. spos .. ';trade;1, 2.5;1, 1;]',
|
'list[nodemeta:' .. spos .. ';trade;1, 2.5;1, 1;]',
|
||||||
@ -536,10 +531,10 @@ function XEnchanting.get_formspec(self, pos, player_name, data)
|
|||||||
|
|
||||||
if inv:get_stack('trade', 1):get_count() >= i then
|
if inv:get_stack('trade', 1):get_count() >= i then
|
||||||
---@diagnostic disable-next-line: codestyle-check
|
---@diagnostic disable-next-line: codestyle-check
|
||||||
formspec[#formspec + 1] = 'image_button[2.5,' .. -0.5 + i .. ';5,1;x_enchanting_image_button.png;slot_' .. i .. ';' .. slot.tool_cap_data.enchantments_desc_masked .. ' ' .. minetest.colorize('#FFFF00', 'level: ' .. slot.level) .. ']'
|
formspec[#formspec + 1] = 'image_button[2.5,' .. -0.5 + i .. ';5,1;x_enchanting_image_button.png;slot_' .. i .. ';' .. slot.tool_cap_data.enchantments_desc_masked .. ' ' .. minetest.colorize('#FFFF00', S('level') .. ': ' .. slot.level) .. ']'
|
||||||
else
|
else
|
||||||
---@diagnostic disable-next-line: codestyle-check
|
---@diagnostic disable-next-line: codestyle-check
|
||||||
formspec[#formspec + 1] = 'image_button[2.5,' .. -0.5 + i .. ';5,1;x_enchanting_image_button_disabled.png;slot_' .. i .. ';' .. slot.tool_cap_data.enchantments_desc_masked .. ' ' .. minetest.colorize('#FFFF00', 'level: ' .. slot.level) .. ']'
|
formspec[#formspec + 1] = 'image_button[2.5,' .. -0.5 + i .. ';5,1;x_enchanting_image_button_disabled.png;slot_' .. i .. ';' .. slot.tool_cap_data.enchantments_desc_masked .. ' ' .. minetest.colorize('#FFFF00', S('level') .. ': ' .. slot.level) .. ']'
|
||||||
end
|
end
|
||||||
|
|
||||||
formspec[#formspec + 1] = 'image[2.5,' .. -0.5 + i .. ';1,1;x_enchanting_image_trade_' .. i .. '.png;]'
|
formspec[#formspec + 1] = 'image[2.5,' .. -0.5 + i .. ';1,1;x_enchanting_image_trade_' .. i .. '.png;]'
|
||||||
|
@ -99,7 +99,7 @@ pipelines:
|
|||||||
- nvm
|
- nvm
|
||||||
script:
|
script:
|
||||||
- nvm use v17.2.0
|
- nvm use v17.2.0
|
||||||
- npm run push:ci -- --token=$CONTENT_DB_X_ENCHATING_TOKEN --title=$BITBUCKET_TAG
|
- npm run push:ci -- --token=$CONTENT_DB_X_ENCHANTING_TOKEN --title=$BITBUCKET_TAG
|
||||||
definitions:
|
definitions:
|
||||||
caches:
|
caches:
|
||||||
node-modules: ./node_modules
|
node-modules: ./node_modules
|
||||||
|
476
i18n.py
Executable file
476
i18n.py
Executable file
@ -0,0 +1,476 @@
|
|||||||
|
#!/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,
|
||||||
|
"print-source": False,
|
||||||
|
"truncate-unused": 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'],
|
||||||
|
"print-source": ['--print-source', '-p'],
|
||||||
|
"truncate-unused": ['--truncate-unused', '-t'],
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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["print-source"])}
|
||||||
|
add comments denoting the source file
|
||||||
|
{', '.join(options["verbose"])}
|
||||||
|
add output information
|
||||||
|
{', '.join(options["truncate-unused"])}
|
||||||
|
delete unused strings from files
|
||||||
|
''')
|
||||||
|
|
||||||
|
|
||||||
|
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.expanduser('~/.minetest/mods/')}")
|
||||||
|
run_all_subfolders(os.path.expanduser("~/.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 or folder name. 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:
|
||||||
|
if not os.path.isfile(os.path.join(folder, "modpack.txt")):
|
||||||
|
folder_name = os.path.basename(folder)
|
||||||
|
# Special case when run in Minetest's builtin directory
|
||||||
|
if folder_name == "builtin":
|
||||||
|
return "__builtin"
|
||||||
|
else:
|
||||||
|
return folder_name
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
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 = f'{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}"]
|
||||||
|
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()
|
||||||
|
if params["print-source"]:
|
||||||
|
if lOut[-1] != "":
|
||||||
|
lOut.append("")
|
||||||
|
lOut.append(source)
|
||||||
|
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 and comment != "" and not comment.startswith("# textdomain:"):
|
||||||
|
lOut.append(comment)
|
||||||
|
lOut.append(f"{localizedString}={translation}")
|
||||||
|
if params["break-long-lines"] and len(localizedString) > doublespace_threshold:
|
||||||
|
lOut.append("")
|
||||||
|
|
||||||
|
|
||||||
|
unusedExist = False
|
||||||
|
if not params["truncate-unused"]:
|
||||||
|
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.startswith("###"):
|
||||||
|
if header_comment is None and not latest_comment_block is None:
|
||||||
|
# Save header comments
|
||||||
|
header_comment = latest_comment_block
|
||||||
|
# Strip 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
|
||||||
|
elif line.startswith("#"):
|
||||||
|
# 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() and not f.name.startswith('.')]
|
||||||
|
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() and not f.name.startswith('.')]:
|
||||||
|
update_folder(modfolder)
|
||||||
|
|
||||||
|
|
||||||
|
main()
|
11
locale/template.txt
Normal file
11
locale/template.txt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# textdomain: x_enchanting
|
||||||
|
Sharpness=
|
||||||
|
Fortune=
|
||||||
|
Unbreaking=
|
||||||
|
Efficiency=
|
||||||
|
Enchanted=
|
||||||
|
Enchant=
|
||||||
|
level=
|
||||||
|
Enchanting Table=
|
||||||
|
owned by=
|
||||||
|
Scroll of Enchantments=
|
11
locale/x_enchanting.sk.tr
Normal file
11
locale/x_enchanting.sk.tr
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# textdomain: x_enchanting
|
||||||
|
Sharpness=Ostrosť
|
||||||
|
Fortune=Šťastie
|
||||||
|
Unbreaking=Nelámavosť
|
||||||
|
Efficiency=Výkonnosť
|
||||||
|
Enchanted=Očarený
|
||||||
|
Enchant=Očarovať
|
||||||
|
level=level
|
||||||
|
Enchanting Table=Čarovný stôl
|
||||||
|
owned by=vlastník
|
||||||
|
Scroll of Enchantments=Čarovný Zvitok
|
18
table.lua
18
table.lua
@ -1,12 +1,14 @@
|
|||||||
screwdriver = minetest.global_exists('screwdriver') and screwdriver --[[@as MtgScrewdriver]]
|
screwdriver = minetest.global_exists('screwdriver') and screwdriver --[[@as MtgScrewdriver]]
|
||||||
|
|
||||||
|
local S = minetest.get_translator(minetest.get_current_modname())
|
||||||
|
|
||||||
----
|
----
|
||||||
--- Table Node
|
--- Table Node
|
||||||
----
|
----
|
||||||
|
|
||||||
minetest.register_node('x_enchanting:table', {
|
minetest.register_node('x_enchanting:table', {
|
||||||
description = 'Enchating Table',
|
description = S('Enchanting Table'),
|
||||||
short_description = 'Enchating Table',
|
short_description = S('Enchanting Table'),
|
||||||
drawtype = 'mesh',
|
drawtype = 'mesh',
|
||||||
mesh = 'x_enchanting_table.obj',
|
mesh = 'x_enchanting_table.obj',
|
||||||
tiles = { 'x_enchanting_table.png' },
|
tiles = { 'x_enchanting_table.png' },
|
||||||
@ -46,7 +48,7 @@ minetest.register_node('x_enchanting:table', {
|
|||||||
local meta = minetest.get_meta(pos)
|
local meta = minetest.get_meta(pos)
|
||||||
local inv = meta:get_inventory()
|
local inv = meta:get_inventory()
|
||||||
|
|
||||||
meta:set_string('infotext', 'Enchating Table')
|
meta:set_string('infotext', S('Enchanting Table'))
|
||||||
meta:set_string('owner', '')
|
meta:set_string('owner', '')
|
||||||
inv:set_size('item', 1)
|
inv:set_size('item', 1)
|
||||||
inv:set_size('trade', 1)
|
inv:set_size('trade', 1)
|
||||||
@ -67,7 +69,7 @@ minetest.register_node('x_enchanting:table', {
|
|||||||
local player_name = placer:get_player_name()
|
local player_name = placer:get_player_name()
|
||||||
|
|
||||||
meta:set_string('owner', player_name)
|
meta:set_string('owner', player_name)
|
||||||
meta:set_string('infotext', 'Enchating Table (owned by ' .. player_name .. ')')
|
meta:set_string('infotext', S('Enchanting Table') .. ' (' .. S('owned by') .. ' ' .. player_name .. ')')
|
||||||
end,
|
end,
|
||||||
---@param pos Vector
|
---@param pos Vector
|
||||||
---@param node NodeDef
|
---@param node NodeDef
|
||||||
@ -216,7 +218,7 @@ minetest.register_node('x_enchanting:table', {
|
|||||||
local bookshelfs = minetest.find_nodes_in_area(
|
local bookshelfs = minetest.find_nodes_in_area(
|
||||||
{ x = pos.x - 2, y = pos.y, z = pos.z - 2 },
|
{ x = pos.x - 2, y = pos.y, z = pos.z - 2 },
|
||||||
{ x = pos.x + 2, y = pos.y + 2, z = pos.z + 2 },
|
{ x = pos.x + 2, y = pos.y + 2, z = pos.z + 2 },
|
||||||
{ 'default:bookshelf' }
|
{ 'default:bookshelf', 'group:bookshelf' }
|
||||||
)
|
)
|
||||||
|
|
||||||
if #bookshelfs == 0 then
|
if #bookshelfs == 0 then
|
||||||
@ -332,7 +334,7 @@ minetest.register_node('x_enchanting:table', {
|
|||||||
local bookshelfs = minetest.find_nodes_in_area(
|
local bookshelfs = minetest.find_nodes_in_area(
|
||||||
{ x = pos.x - 2, y = pos.y, z = pos.z - 2 },
|
{ x = pos.x - 2, y = pos.y, z = pos.z - 2 },
|
||||||
{ x = pos.x + 2, y = pos.y + 2, z = pos.z + 2 },
|
{ x = pos.x + 2, y = pos.y + 2, z = pos.z + 2 },
|
||||||
{ 'default:bookshelf' }
|
{ 'default:bookshelf', 'group:bookshelf' }
|
||||||
)
|
)
|
||||||
|
|
||||||
local item_stack = inv:get_stack('item', 1)
|
local item_stack = inv:get_stack('item', 1)
|
||||||
@ -360,7 +362,7 @@ minetest.register_node('x_enchanting:table', {
|
|||||||
local bookshelfs = minetest.find_nodes_in_area(
|
local bookshelfs = minetest.find_nodes_in_area(
|
||||||
{ x = pos.x - 2, y = pos.y, z = pos.z - 2 },
|
{ x = pos.x - 2, y = pos.y, z = pos.z - 2 },
|
||||||
{ x = pos.x + 2, y = pos.y + 2, z = pos.z + 2 },
|
{ x = pos.x + 2, y = pos.y + 2, z = pos.z + 2 },
|
||||||
{ 'default:bookshelf' }
|
{ 'default:bookshelf', 'group:bookshelf' }
|
||||||
)
|
)
|
||||||
|
|
||||||
local item_stack = inv:get_stack('item', 1)
|
local item_stack = inv:get_stack('item', 1)
|
||||||
@ -457,7 +459,7 @@ minetest.register_entity('x_enchanting:table_scroll', {
|
|||||||
visual_size = { x = 1, y = 1, z = 1 },
|
visual_size = { x = 1, y = 1, z = 1 },
|
||||||
glow = 1,
|
glow = 1,
|
||||||
pointable = false,
|
pointable = false,
|
||||||
infotext = 'Scroll of Enchantments',
|
infotext = S('Scroll of Enchantments'),
|
||||||
},
|
},
|
||||||
---@param self table
|
---@param self table
|
||||||
---@param killer ObjectRef
|
---@param killer ObjectRef
|
||||||
|
@ -10,13 +10,13 @@
|
|||||||
---@field get_enchanted_tool_capabilities fun(self: XEnchanting, tool_def: ItemDef, enchantments: Enchantments[]): GetEnchantedToolCapabilitiesReturn Applies enchantments to item tool capabilities.
|
---@field get_enchanted_tool_capabilities fun(self: XEnchanting, tool_def: ItemDef, enchantments: Enchantments[]): GetEnchantedToolCapabilitiesReturn Applies enchantments to item tool capabilities.
|
||||||
---@field set_enchanted_tool fun(self: XEnchanting, pos: Vector, itemstack: ItemStack, capabilities: ToolCapabilitiesDef, description: string, level: number, player_name: string): nil Set choosen enchantment and its modified tool capabilities to itemstack and `item` inventory. This will also get new `randomseed`.
|
---@field set_enchanted_tool fun(self: XEnchanting, pos: Vector, itemstack: ItemStack, capabilities: ToolCapabilitiesDef, description: string, level: number, player_name: string): nil Set choosen enchantment and its modified tool capabilities to itemstack and `item` inventory. This will also get new `randomseed`.
|
||||||
---@field get_enchantment_data fun(self: XEnchanting, nr_of_bookshelfs: number, tool_def: ItemDef): EnchantmentData Algoritm to get aplicable random enchantments.
|
---@field get_enchantment_data fun(self: XEnchanting, nr_of_bookshelfs: number, tool_def: ItemDef): EnchantmentData Algoritm to get aplicable random enchantments.
|
||||||
---@field get_formspec fun(self: XEnchanting, pos, player_name, data): string Builds and returns `formspec` string
|
---@field get_formspec fun(self: XEnchanting, pos: Vector, player_name: string, data?: EnchantmentData): string Builds and returns `formspec` string
|
||||||
|
|
||||||
|
|
||||||
---Enchantment definition
|
---Enchantment definition
|
||||||
---@class EnchantmentDef
|
---@class EnchantmentDef
|
||||||
---@field name string Readable name of the enchantment
|
---@field name string Readable name of the enchantment
|
||||||
---@field final_level_range table<number, table<number, number[]>> Level range
|
---@field final_level_range table<number, number[]> Level range
|
||||||
---@field level_def table<number, number> Level bonus
|
---@field level_def table<number, number> Level bonus
|
||||||
---@field weight number Enchantment weight
|
---@field weight number Enchantment weight
|
||||||
---@field randomseed number Math random seed. Will change after item was enchanted.
|
---@field randomseed number Math random seed. Will change after item was enchanted.
|
||||||
@ -47,7 +47,7 @@
|
|||||||
|
|
||||||
|
|
||||||
---@class ToolCapData
|
---@class ToolCapData
|
||||||
---@field enchantments_desc string Masket description before enchating
|
---@field enchantments_desc string Masket description before enchanting
|
||||||
---@field enchantments_desc_masked string Description added to item description definition after enchanting
|
---@field enchantments_desc_masked string Description added to item description definition after enchanting
|
||||||
---@field tool_capabilities ToolCapabilitiesDef Modified tool capabilities with applied enchantment
|
---@field tool_capabilities ToolCapabilitiesDef Modified tool capabilities with applied enchantment
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user