From df13b27260eebfec74c0930fe199fd15a094219a Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Mon, 17 Feb 2020 23:07:10 -0700 Subject: [PATCH] switch to native translator --- i18n.py | 218 ++++++++++++++++++++++++++++++++++++ init.lua | 8 +- intllib.lua | 45 -------- locale/de.po | 37 ------ locale/dynamic_liquid.de.tr | 7 ++ locale/dynamic_liquid.es.tr | 7 ++ locale/dynamic_liquid.fr.tr | 7 ++ locale/es.po | 43 ------- locale/fr.po | 37 ------ locale/template.pot | 37 ------ locale/template.txt | 4 + mod.conf | 4 +- 12 files changed, 250 insertions(+), 204 deletions(-) create mode 100644 i18n.py delete mode 100644 intllib.lua delete mode 100644 locale/de.po create mode 100644 locale/dynamic_liquid.de.tr create mode 100644 locale/dynamic_liquid.es.tr create mode 100644 locale/dynamic_liquid.fr.tr delete mode 100644 locale/es.po delete mode 100644 locale/fr.po delete mode 100644 locale/template.pot create mode 100644 locale/template.txt diff --git a/i18n.py b/i18n.py new file mode 100644 index 0000000..957804a --- /dev/null +++ b/i18n.py @@ -0,0 +1,218 @@ +#!/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[ ]*=[ ]*([^ \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(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 + +# 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(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): + print(tr_name + " already exists, ignoring " + name) + continue + fname = os.path.join(root, name) + with open(fname, "r", encoding='utf-8') as po_file: + print("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 + +# 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: + process_po_files(folder, modname) + 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 + "/") diff --git a/init.lua b/init.lua index c70fe55..1c7b0eb 100644 --- a/init.lua +++ b/init.lua @@ -5,11 +5,11 @@ dynamic_liquid.registered_liquid_neighbors = {} local water_level = tonumber(minetest.get_mapgen_setting("water_level")) --- internationalization boilerplate -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP.."/intllib.lua") +local modname = minetest.get_current_modname() +local modpath = minetest.get_modpath(modname) +local S = minetest.get_translator(modname) -dofile(MP.."/cooling_lava.lua") +dofile(modpath.."/cooling_lava.lua") -- By making this giant table of all possible permutations of horizontal direction we can avoid -- lots of redundant calculations. diff --git a/intllib.lua b/intllib.lua deleted file mode 100644 index 6669d72..0000000 --- a/intllib.lua +++ /dev/null @@ -1,45 +0,0 @@ - --- Fallback functions for when `intllib` is not installed. --- Code released under Unlicense . - --- Get the latest version of this file at: --- https://raw.githubusercontent.com/minetest-mods/intllib/master/lib/intllib.lua - -local function format(str, ...) - local args = { ... } - local function repl(escape, open, num, close) - if escape == "" then - local replacement = tostring(args[tonumber(num)]) - if open == "" then - replacement = replacement..close - end - return replacement - else - return "@"..open..num..close - end - end - return (str:gsub("(@?)@(%(?)(%d+)(%)?)", repl)) -end - -local gettext, ngettext -if minetest.get_modpath("intllib") then - if intllib.make_gettext_pair then - -- New method using gettext. - gettext, ngettext = intllib.make_gettext_pair() - else - -- Old method using text files. - gettext = intllib.Getter() - end -end - --- Fill in missing functions. - -gettext = gettext or function(msgid, ...) - return format(msgid, ...) -end - -ngettext = ngettext or function(msgid, msgid_plural, n, ...) - return format(n==1 and msgid or msgid_plural, ...) -end - -return gettext, ngettext diff --git a/locale/de.po b/locale/de.po deleted file mode 100644 index adf6cdb..0000000 --- a/locale/de.po +++ /dev/null @@ -1,37 +0,0 @@ -# Dynamic liquid Minetest mod. -# Copyright (C) 2017 -# This file is distributed under the same license as the dynamic_liquid package. -# FaceDeer -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-02-12 12:55-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:141 -msgid "Damp Clay" -msgstr "Feuchten Lehm" - -#: init.lua:197 -msgid "Spring" -msgstr "Quelle" - -#: init.lua:198 -msgid "" -"A natural spring that generates an endless stream of water source blocks" -msgstr "" - -#: init.lua:199 -msgid "" -"Generates one source block of water directly on top of itself once per " -"second, provided the space is clear. If this natural spring is dug out the " -"flow stops and it is turned into ordinary cobble." -msgstr "" diff --git a/locale/dynamic_liquid.de.tr b/locale/dynamic_liquid.de.tr new file mode 100644 index 0000000..a29a278 --- /dev/null +++ b/locale/dynamic_liquid.de.tr @@ -0,0 +1,7 @@ +# textdomain: dynamic_liquid + +A natural spring that generates an endless stream of water source blocks= +Damp Clay=Feuchten Lehm +Generates one source block of water directly on top of itself once per second, provided the space is clear. If this natural spring is dug out the flow stops and it is turned into ordinary cobble.= +Spring=Quelle +##### not used anymore ##### \ No newline at end of file diff --git a/locale/dynamic_liquid.es.tr b/locale/dynamic_liquid.es.tr new file mode 100644 index 0000000..8af9585 --- /dev/null +++ b/locale/dynamic_liquid.es.tr @@ -0,0 +1,7 @@ +# textdomain: dynamic_liquid + +A natural spring that generates an endless stream of water source blocks=Un manantial natural que genera un flujo sin fín de bloques fuente de agua +Damp Clay=Arcilla húmeda +Generates one source block of water directly on top of itself once per second, provided the space is clear. If this natural spring is dug out the flow stops and it is turned into ordinary cobble.=Genera un bloque fuente de agua directamente encima de sí mismo una vez por segundo, en tanto el espacio esté libre. Si éste manantial natural es excavado el flujo se detiene y se convierte en adoquines comúnes. +Spring=Manantial +##### not used anymore ##### \ No newline at end of file diff --git a/locale/dynamic_liquid.fr.tr b/locale/dynamic_liquid.fr.tr new file mode 100644 index 0000000..c730ba0 --- /dev/null +++ b/locale/dynamic_liquid.fr.tr @@ -0,0 +1,7 @@ +# textdomain: dynamic_liquid + +A natural spring that generates an endless stream of water source blocks= +Damp Clay=Argile humide +Generates one source block of water directly on top of itself once per second, provided the space is clear. If this natural spring is dug out the flow stops and it is turned into ordinary cobble.= +Spring=Source +##### not used anymore ##### \ No newline at end of file diff --git a/locale/es.po b/locale/es.po deleted file mode 100644 index dc6f807..0000000 --- a/locale/es.po +++ /dev/null @@ -1,43 +0,0 @@ -# Spanish translations for PACKAGE package -# Traducciones al español para el paquete PACKAGE. -# Copyright (C) 2017 THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# Diego Martínez , 2017. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-02-12 12:55-0700\n" -"PO-Revision-Date: 2017-02-24 00:13-0300\n" -"Last-Translator: Diego Martínez \n" -"Language-Team: Spanish\n" -"Language: es\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: init.lua:141 -msgid "Damp Clay" -msgstr "Arcilla húmeda" - -#: init.lua:197 -msgid "Spring" -msgstr "Manantial" - -#: init.lua:198 -msgid "" -"A natural spring that generates an endless stream of water source blocks" -msgstr "" -"Un manantial natural que genera un flujo sin fín de bloques fuente de agua" - -#: init.lua:199 -msgid "" -"Generates one source block of water directly on top of itself once per " -"second, provided the space is clear. If this natural spring is dug out the " -"flow stops and it is turned into ordinary cobble." -msgstr "" -"Genera un bloque fuente de agua directamente encima de sí mismo una vez por " -"segundo, en tanto el espacio esté libre. Si éste manantial natural es " -"excavado el flujo se detiene y se convierte en adoquines comúnes." diff --git a/locale/fr.po b/locale/fr.po deleted file mode 100644 index 3b7c85a..0000000 --- a/locale/fr.po +++ /dev/null @@ -1,37 +0,0 @@ -# Dynamic liquid Minetest mod. -# Copyright (C) 2017 -# This file is distributed under the same license as the dynamic_liquid package. -# FaceDeer -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-02-12 12:55-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:141 -msgid "Damp Clay" -msgstr "Argile humide" - -#: init.lua:197 -msgid "Spring" -msgstr "Source" - -#: init.lua:198 -msgid "" -"A natural spring that generates an endless stream of water source blocks" -msgstr "" - -#: init.lua:199 -msgid "" -"Generates one source block of water directly on top of itself once per " -"second, provided the space is clear. If this natural spring is dug out the " -"flow stops and it is turned into ordinary cobble." -msgstr "" diff --git a/locale/template.pot b/locale/template.pot deleted file mode 100644 index 90e9c57..0000000 --- a/locale/template.pot +++ /dev/null @@ -1,37 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-02-12 12:55-0700\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" -"Content-Transfer-Encoding: 8bit\n" - -#: init.lua:141 -msgid "Damp Clay" -msgstr "" - -#: init.lua:197 -msgid "Spring" -msgstr "" - -#: init.lua:198 -msgid "" -"A natural spring that generates an endless stream of water source blocks" -msgstr "" - -#: init.lua:199 -msgid "" -"Generates one source block of water directly on top of itself once per " -"second, provided the space is clear. If this natural spring is dug out the " -"flow stops and it is turned into ordinary cobble." -msgstr "" diff --git a/locale/template.txt b/locale/template.txt new file mode 100644 index 0000000..8f13c3d --- /dev/null +++ b/locale/template.txt @@ -0,0 +1,4 @@ +A natural spring that generates an endless stream of water source blocks= +Damp Clay= +Generates one source block of water directly on top of itself once per second, provided the space is clear. If this natural spring is dug out the flow stops and it is turned into ordinary cobble.= +Spring= \ No newline at end of file diff --git a/mod.conf b/mod.conf index 875df42..99e915e 100644 --- a/mod.conf +++ b/mod.conf @@ -1 +1,3 @@ -name = dynamic_liquid \ No newline at end of file +name = dynamic_liquid +optional_depends = default, doc, xpanes, carts +description = Flowing dynamic liquids and ocean-maintenance springs. \ No newline at end of file