update translation system
This commit is contained in:
parent
328d8512c2
commit
90e4d6130d
168
i18n.py
Normal file
168
i18n.py
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- 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
|
||||||
|
# LGPLv2.1+
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
import os, fnmatch, re, shutil, errno
|
||||||
|
|
||||||
|
#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
|
||||||
|
#TODO: support [[]] delimiters
|
||||||
|
pattern_lua = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\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[ ]*=[ ]*([^ ]*)')
|
||||||
|
pattern_tr_filename = re.compile(r'\.tr$')
|
||||||
|
|
||||||
|
#attempt to read the mod's name from the mod.conf file. Returns None on failure
|
||||||
|
def get_modname(folder):
|
||||||
|
try:
|
||||||
|
with open(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(folder + 'locale/'):
|
||||||
|
for name in files:
|
||||||
|
if pattern_tr_filename.search(name):
|
||||||
|
out.append(name)
|
||||||
|
return out
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Writes a template.txt file
|
||||||
|
def write_template(templ_file, lkeyStrings):
|
||||||
|
lOut = []
|
||||||
|
lkeyStrings.sort()
|
||||||
|
for s in lkeyStrings:
|
||||||
|
lOut.append("%s=" % s)
|
||||||
|
mkdir_p(os.path.dirname(templ_file))
|
||||||
|
with open(templ_file, "wt", encoding='utf-8') as template_file:
|
||||||
|
template_file.write("\n".join(lOut))
|
||||||
|
|
||||||
|
# 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()
|
||||||
|
text = re.sub(pattern_concat, "", text)
|
||||||
|
for s in pattern_lua.findall(text):
|
||||||
|
s = s[1]
|
||||||
|
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
|
||||||
|
def import_tr_file(tr_file):
|
||||||
|
dOut = {}
|
||||||
|
if os.path.exists(tr_file):
|
||||||
|
with open(tr_file, "r", encoding='utf-8') as existing_file :
|
||||||
|
for line in existing_file.readlines():
|
||||||
|
s = line.strip()
|
||||||
|
if s == "" or s[0] == "#":
|
||||||
|
continue
|
||||||
|
match = pattern_tr.match(s)
|
||||||
|
if match:
|
||||||
|
dOut[match.group(1)] = match.group(2)
|
||||||
|
return dOut
|
||||||
|
|
||||||
|
# Walks all lua files in the mod folder, collects translatable strings,
|
||||||
|
# and writes it to a template.txt file
|
||||||
|
def generate_template(folder):
|
||||||
|
lOut = []
|
||||||
|
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)
|
||||||
|
print(fname + ": " + str(len(found)) + " translatable strings")
|
||||||
|
lOut.extend(found)
|
||||||
|
lOut = list(set(lOut))
|
||||||
|
lOut.sort()
|
||||||
|
if len(lOut) == 0:
|
||||||
|
return None
|
||||||
|
templ_file = folder + "locale/template.txt"
|
||||||
|
write_template(templ_file, lOut)
|
||||||
|
return lOut
|
||||||
|
|
||||||
|
# Updates an existing .tr file, copying the old one to a ".old" file
|
||||||
|
def update_tr_file(lNew, mod_name, tr_file):
|
||||||
|
print("updating " + tr_file)
|
||||||
|
lOut = ["# textdomain: %s\n" % mod_name]
|
||||||
|
|
||||||
|
#TODO only make a .old if there are actual changes from the old file
|
||||||
|
if os.path.exists(tr_file):
|
||||||
|
shutil.copyfile(tr_file, tr_file+".old")
|
||||||
|
|
||||||
|
dOld = import_tr_file(tr_file)
|
||||||
|
for key in lNew:
|
||||||
|
val = dOld.get(key, "")
|
||||||
|
lOut.append("%s=%s" % (key, val))
|
||||||
|
lOut.append("##### not used anymore #####")
|
||||||
|
for key in dOld:
|
||||||
|
if key not in lNew:
|
||||||
|
lOut.append("%s=%s" % (key, dOld[key]))
|
||||||
|
with open(tr_file, "w", encoding='utf-8') as new_tr_file:
|
||||||
|
new_tr_file.write("\n".join(lOut))
|
||||||
|
|
||||||
|
# Updates translation files for the mod in the given folder
|
||||||
|
def update_mod(folder):
|
||||||
|
modname = get_modname(folder)
|
||||||
|
if modname is not None:
|
||||||
|
print("Updating translations for " + modname)
|
||||||
|
data = generate_template(folder)
|
||||||
|
if data == None:
|
||||||
|
print("No translatable strings found in " + modname)
|
||||||
|
else:
|
||||||
|
for tr_file in get_existing_tr_files(folder):
|
||||||
|
update_tr_file(data, modname, folder + "locale/" + tr_file)
|
||||||
|
else:
|
||||||
|
print("Unable to find modname in folder " + folder)
|
||||||
|
|
||||||
|
def update_folder(folder):
|
||||||
|
is_modpack = os.path.exists(folder+"modpack.txt") or os.path.exists(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.")
|
||||||
|
|
||||||
|
|
||||||
|
update_folder("./")
|
||||||
|
|
||||||
|
# Runs this script on each sub-folder in the parent folder.
|
||||||
|
# I'm using this for testing this script on all installed mods.
|
||||||
|
#for modfolder in [f.path for f in os.scandir("../") if f.is_dir()]:
|
||||||
|
# update_folder(modfolder + "/")
|
16
init.lua
16
init.lua
@ -9,9 +9,7 @@
|
|||||||
-- When a blank tag stack is used to punch an in-world tag, it inherits that tag's values (continues the chain)
|
-- When a blank tag stack is used to punch an in-world tag, it inherits that tag's values (continues the chain)
|
||||||
-- Can turn a tag stack blank again via crafting menu
|
-- Can turn a tag stack blank again via crafting menu
|
||||||
|
|
||||||
-- internationalization boilerplate
|
local S = minetest.get_translator(minetest.get_current_modname())
|
||||||
local MP = minetest.get_modpath(minetest.get_current_modname())
|
|
||||||
local S, NS = dofile(MP.."/intllib.lua")
|
|
||||||
|
|
||||||
local glow = minetest.setting_get("breadcrumbs_glow_in_the_dark")
|
local glow = minetest.setting_get("breadcrumbs_glow_in_the_dark")
|
||||||
local glow_level
|
local glow_level
|
||||||
@ -147,9 +145,6 @@ minetest.register_craftitem("breadcrumbs:blank", {
|
|||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
local placed_by_text = S("%s #%d\nPlaced by %s")
|
|
||||||
local distance_from_text = S("%dm from last marker")
|
|
||||||
|
|
||||||
minetest.register_node("breadcrumbs:marker", {
|
minetest.register_node("breadcrumbs:marker", {
|
||||||
description = S("Marker"),
|
description = S("Marker"),
|
||||||
_doc_items_longdesc = marker_longdesc,
|
_doc_items_longdesc = marker_longdesc,
|
||||||
@ -201,12 +196,11 @@ minetest.register_node("breadcrumbs:marker", {
|
|||||||
if number > 1 and previous_pos_string ~= "" then
|
if number > 1 and previous_pos_string ~= "" then
|
||||||
local previous_pos = minetest.string_to_pos(previous_pos_string)
|
local previous_pos = minetest.string_to_pos(previous_pos_string)
|
||||||
node_meta:set_string("previous_pos", previous_pos_string)
|
node_meta:set_string("previous_pos", previous_pos_string)
|
||||||
local dist = vector.distance(pos, previous_pos)
|
local dist = math.floor(vector.distance(pos, previous_pos))
|
||||||
node_meta:set_string("infotext",
|
node_meta:set_string("infotext", S("@1 #@2\nPlaced by @3", label, number, playername)
|
||||||
string.format(placed_by_text .. "\n" .. distance_from_text, label, number, playername, dist))
|
.. "\n" .. S("@1m from last marker", dist))
|
||||||
else
|
else
|
||||||
node_meta:set_string("infotext",
|
node_meta:set_string("infotext", S("@1 #@2\nPlaced by @3", label, number, playername))
|
||||||
string.format(placed_by_text, label, number, playername))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local item_meta = itemstack:get_meta()
|
local item_meta = itemstack:get_meta()
|
||||||
|
45
intllib.lua
45
intllib.lua
@ -1,45 +0,0 @@
|
|||||||
|
|
||||||
-- Fallback functions for when `intllib` is not installed.
|
|
||||||
-- Code released under Unlicense <http://unlicense.org>.
|
|
||||||
|
|
||||||
-- 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
|
|
@ -1,86 +0,0 @@
|
|||||||
# Breadcrumbs mod for Minetest.
|
|
||||||
# Copyright (C) 2017
|
|
||||||
# This file is distributed under the same license as the Breadcrumbs package.
|
|
||||||
# FaceDeer
|
|
||||||
#
|
|
||||||
#, fuzzy
|
|
||||||
msgid ""
|
|
||||||
msgstr ""
|
|
||||||
"Project-Id-Version: \n"
|
|
||||||
"Report-Msgid-Bugs-To: \n"
|
|
||||||
"POT-Creation-Date: 2017-02-02 20:39-0700\n"
|
|
||||||
"PO-Revision-Date: \n"
|
|
||||||
"Last-Translator: \n"
|
|
||||||
"Language-Team: \n"
|
|
||||||
"MIME-Version: 1.0\n"
|
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
|
||||||
|
|
||||||
#: init.lua:40
|
|
||||||
msgid "A blank path marker sign, ready to have a label affixed"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: init.lua:41
|
|
||||||
msgid ""
|
|
||||||
"To start marking a new path, wield a stack of blank markers. You'll be "
|
|
||||||
"presented with a form to fill in a short text label that this path will "
|
|
||||||
"bear, after which you can begin placing path markers as you explore. You can "
|
|
||||||
"also use a blank marker stack on an existing path marker that's already been "
|
|
||||||
"placed and you'll copy the marker's label and continue the path from that "
|
|
||||||
"point when laying down new markers from your copied stack."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: init.lua:43
|
|
||||||
msgid "A path marker with a label affixed"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: init.lua:44
|
|
||||||
msgid ""
|
|
||||||
"This marker has had a label assigned and is counting the markers you've been "
|
|
||||||
"laying down."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: init.lua:46
|
|
||||||
msgid ""
|
|
||||||
"Each marker knows the location of the previous marker in your path, and "
|
|
||||||
"right-clicking on it will cause it to emit a stream of indicators that only "
|
|
||||||
"you can see pointing the direction it lies in."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: init.lua:48
|
|
||||||
msgid ""
|
|
||||||
"If you place a marker incorrectly you can \"undo\" the placement by clicking "
|
|
||||||
"on it with the stack you used to place it. Otherwise, markers can only be "
|
|
||||||
"removed with an axe. Labeled markers can be turned back into blank markers "
|
|
||||||
"via the crafting grid."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: init.lua:50
|
|
||||||
msgid "Label:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: init.lua:51
|
|
||||||
msgid "Save"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: init.lua:95
|
|
||||||
msgid "Blank Marker"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# The first %s will be replaced by the label text, the %d by the marker count, and the second %s by the name of the player
|
|
||||||
#: init.lua:118
|
|
||||||
#, c-format
|
|
||||||
msgid ""
|
|
||||||
"%s #%d\n"
|
|
||||||
"Placed by %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# The %d will be replaced with the number of meters this marker is from the previous marker in this path
|
|
||||||
#: init.lua:119
|
|
||||||
#, c-format
|
|
||||||
msgid "%dm from last marker"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: init.lua:122
|
|
||||||
msgid "Marker"
|
|
||||||
msgstr ""
|
|
12
locale/template.txt
Normal file
12
locale/template.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
@1 #@2@nPlaced by @3=
|
||||||
|
@1m from last marker=
|
||||||
|
A blank path marker sign, ready to have a label affixed=
|
||||||
|
A path marker with a label affixed=
|
||||||
|
Blank Marker=
|
||||||
|
Each marker knows the location of the previous marker in your path, and right-clicking on it will cause it to emit a stream of indicators that only you can see pointing the direction it lies in.=
|
||||||
|
If you place a marker incorrectly you can "undo" the placement by clicking on it with the stack you used to place it. Otherwise, markers can only be removed with an axe. Labeled markers can be turned back into blank markers via the crafting grid.=
|
||||||
|
Label:=
|
||||||
|
Marker=
|
||||||
|
Save=
|
||||||
|
This marker has had a label assigned and is counting the markers you've been laying down.=
|
||||||
|
To start marking a new path, wield a stack of blank markers. You'll be presented with a form to fill in a short text label that this path will bear, after which you can begin placing path markers as you explore. You can also use a blank marker stack on an existing path marker that's already been placed and you'll copy the marker's label and continue the path from that point when laying down new markers from your copied stack.=
|
2
mod.conf
2
mod.conf
@ -1,3 +1,3 @@
|
|||||||
name = breadcrumbs
|
name = breadcrumbs
|
||||||
description = Path marker signs for use when exploring a twisty maze of passages that are all alike.
|
description = Path marker signs for use when exploring a twisty maze of passages that are all alike.
|
||||||
optional_depends = default, doc, intllib, loot
|
optional_depends = default, doc, loot
|
Loading…
x
Reference in New Issue
Block a user