update translation system

This commit is contained in:
FaceDeer 2020-02-16 22:14:30 -07:00
parent 328d8512c2
commit 90e4d6130d
6 changed files with 186 additions and 143 deletions

168
i18n.py Normal file
View 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 + "/")

View File

@ -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)
-- Can turn a tag stack blank again via crafting menu
-- internationalization boilerplate
local MP = minetest.get_modpath(minetest.get_current_modname())
local S, NS = dofile(MP.."/intllib.lua")
local S = minetest.get_translator(minetest.get_current_modname())
local glow = minetest.setting_get("breadcrumbs_glow_in_the_dark")
local glow_level
@ -147,9 +145,6 @@ minetest.register_craftitem("breadcrumbs:blank", {
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", {
description = S("Marker"),
_doc_items_longdesc = marker_longdesc,
@ -201,12 +196,11 @@ minetest.register_node("breadcrumbs:marker", {
if number > 1 and previous_pos_string ~= "" then
local previous_pos = minetest.string_to_pos(previous_pos_string)
node_meta:set_string("previous_pos", previous_pos_string)
local dist = vector.distance(pos, previous_pos)
node_meta:set_string("infotext",
string.format(placed_by_text .. "\n" .. distance_from_text, label, number, playername, dist))
local dist = math.floor(vector.distance(pos, previous_pos))
node_meta:set_string("infotext", S("@1 #@2\nPlaced by @3", label, number, playername)
.. "\n" .. S("@1m from last marker", dist))
else
node_meta:set_string("infotext",
string.format(placed_by_text, label, number, playername))
node_meta:set_string("infotext", S("@1 #@2\nPlaced by @3", label, number, playername))
end
local item_meta = itemstack:get_meta()

View File

@ -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

View File

@ -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
View 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.=

View File

@ -1,3 +1,3 @@
name = breadcrumbs
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