#!/usr/bin/env python3 """Genera qml/js/i18n.js e qml/js/catalog.js dai sorgenti Flutter di Henflow. - i18n.js: le 4 mappe di traduzione (it/en/fr/de, 111 chiavi) estratte verbatim da lib/app_language.dart + la funzione t(key, arg, lang). - catalog.js: le 7 razze di lib/models/breed_catalog.dart con il path delle foto installate nel datadir dell'app. I file generati sono ES5 (motore QML di Qt 5.6) con guard module.exports. """ import json import os import re SRC = "/home/kaneda/workspace/henflow-flutter" DST = "/home/kaneda/workspace/harbour-henflow/qml/js" LANGS = ["it", "en", "fr", "de"] def dart_unescape(s): out = [] i = 0 while i < len(s): c = s[i] if c == "\\" and i + 1 < len(s): n = s[i + 1] out.append({"n": "\n", "t": "\t", "r": "\r", "'": "'", '"': '"', "\\": "\\"}.get(n, n)) i += 2 else: out.append(c) i += 1 return "".join(out) def extract_strings(): # NB: la tabella VIVA e' quella inline in main.dart (126 chiavi). # app_language.dart e' una copia vecchia (111 chiavi, usata solo da # lib/dialogs/confirm_dialogs.dart) con gli a capo scritti '\\n' invece # che '\n': estrarla produrrebbe testi con un backslash visibile. text = open(os.path.join(SRC, "lib/main.dart"), encoding="utf-8").read() data = {} for lang in LANGS: m = re.search(r"AppLanguage\.%s:\s*\{(.*?)\n \}," % lang, text, re.S) if not m: raise SystemExit("blocco %s non trovato" % lang) pairs = re.findall(r"'([a-z0-9_]+)':\s*'((?:[^'\\]|\\.)*)'", m.group(1)) data[lang] = {k: dart_unescape(v) for k, v in pairs} print("%s: %d chiavi" % (lang, len(data[lang]))) # L'app non ha fallback: t() su chiave assente restituisce la CHIAVE # (in francese la razza "generic" usciva come "name_generic"). Nel porting # i buchi si colmano con l'inglese e si segnalano qui. keys = set(data["en"]) for lang in LANGS: if lang == "en": continue missing = sorted(keys - set(data[lang])) extra = sorted(set(data[lang]) - keys) if extra: raise SystemExit("%s: chiavi non presenti in inglese: %s" % (lang, extra)) for k in missing: data[lang][k] = data["en"][k] if missing: print(" %s: colmate dall'inglese %d chiavi: %s" % (lang, len(missing), missing)) return data, len(keys) def extract_catalog(): text = open(os.path.join(SRC, "lib/models/breed_catalog.dart"), encoding="utf-8").read() items = [] for block in re.findall(r"BreedCatalogItem\((.*?)\)\s*,?\s*(?=BreedCatalogItem|\];)", text, re.S): def field(name): m = re.search(r"%s:\s*'([^']*)'" % name, block) return m.group(1) if m else None def number(name): m = re.search(r"%s:\s*([0-9.]+)" % name, block) return float(m.group(1)) if m else None asset = field("assetPath") items.append({ "id": field("id"), "name": field("name"), "description": field("description"), "eggColor": field("eggColor"), "eggsPerHenPerDay": number("eggsPerHenPerDay"), "asset": "/usr/share/harbour-henflow/" + asset.replace("assets/", ""), }) return items def js_value(v, indent=4): return json.dumps(v, ensure_ascii=False, indent=indent) def main(): data, nkeys = extract_strings() os.makedirs(DST, exist_ok=True) header = """// GENERATO da tools/gen_from_flutter.py — non modificare a mano. // Traduzioni estratte verbatim da lib/app_language.dart dell'app Flutter // (%d chiavi x %d lingue). ES5: il motore QML di Qt 5.6 non supporta // let/const, arrow function, template literal. var HF_STRINGS = %s; // t(chiave, argomento, lingua): come il TranslationService dell'app, // riempie il primo '{}' (o i successivi, con un array). Fallback: inglese, // poi la chiave stessa. function t(key, arg, lang) { var table = HF_STRINGS[lang] || HF_STRINGS.en; var text = table[key]; if (text === undefined) text = HF_STRINGS.en[key]; if (text === undefined) return key; if (arg !== undefined && arg !== null) { if (Object.prototype.toString.call(arg) === "[object Array]") { for (var i = 0; i < arg.length; i++) { text = text.replace("{}", String(arg[i])); } } else { text = text.replace("{}", String(arg)); } } return text; } function availableLanguages() { var out = []; for (var k in HF_STRINGS) { if (HF_STRINGS.hasOwnProperty(k)) out.push(k); } return out.sort(); } if (typeof module !== "undefined" && module.exports) { module.exports = { HF_STRINGS: HF_STRINGS, t: t, availableLanguages: availableLanguages }; } """ % (nkeys, len(LANGS), js_value(data)) open(os.path.join(DST, "i18n.js"), "w", encoding="utf-8").write(header) catalog = extract_catalog() cat_js = """// GENERATO da tools/gen_from_flutter.py — non modificare a mano. // Catalogo razze da lib/models/breed_catalog.dart: nome e descrizione sono // chiavi di traduzione, le foto stanno nel datadir dell'app. var HF_BREEDS = %s; function breedFromId(id) { for (var i = 0; i < HF_BREEDS.length; i++) { if (HF_BREEDS[i].id === id) return HF_BREEDS[i]; } return null; } if (typeof module !== "undefined" && module.exports) { module.exports = { HF_BREEDS: HF_BREEDS, breedFromId: breedFromId }; } """ % json.dumps(catalog, ensure_ascii=False, indent=4) open(os.path.join(DST, "catalog.js"), "w", encoding="utf-8").write(cat_js) print("scritti: i18n.js (%d chiavi x %d lingue), catalog.js (%d razze)" % (nkeys, len(LANGS), len(catalog))) for c in catalog: print(" ", c["id"], "->", c["asset"]) if __name__ == "__main__": main()