bfd9b2a7ea
gates / gates (push) Successful in 6m38s
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
110 lines
4.4 KiB
Python
110 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""labelcheck.py — every quoted label in a guide draft, looked up in the app's source AT THE PINNED TAG.
|
|
|
|
python3 labelcheck.py <draft.json> <out.txt> <corpus-dir-or-file> [<corpus> ...]
|
|
|
|
A label is the text between „ and ” (or “ and ”) in any guide string, Hungarian or English. It is FOUND
|
|
when it occurs DELIMITED in a corpus file: as a whole JSON key or value, inside QBT_TR(…)QBT_TR, between
|
|
quotes, or between > and <. A bare substring is not enough ("Add" is inside every file). The first hit's
|
|
file:line is recorded. Exit 0 = every label found, 1 = at least one MISSING (the guide stays hidden).
|
|
|
|
The corpus is the app's upstream source at the image's tag (or the files the image itself ships), plus the
|
|
controller's own locale files for the Felhom labels a guide points at (a tab name, „Kész, beállítottam").
|
|
"""
|
|
import io
|
|
import json
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
LABEL = re.compile(r"[„“]([^”„“]+)”")
|
|
TEXT_EXT = (".json", ".js", ".jsx", ".ts", ".tsx", ".html", ".htm", ".vue", ".svelte", ".py", ".go", ".php",
|
|
".twig", ".blade.php", ".po", ".pot", ".yml", ".yaml", ".xml", ".ts.xml", ".properties", ".dart",
|
|
".kt", ".java", ".cs", ".rb", ".erb", ".hbs", ".mjs", ".cjs", ".svg", ".md", ".txt", ".ini", ".toml",
|
|
".strings", ".resx", ".arb", ".csv", ".tmpl", ".gohtml", ".j2", ".jinja", ".lua", ".rs", ".ex", ".exs",
|
|
".heex", ".eex", ".elm", ".swift", ".c", ".cpp", ".h", ".ui")
|
|
|
|
|
|
def labels_of(draft):
|
|
out = []
|
|
for lang in ("hu", "en"):
|
|
g = draft.get(lang) or {}
|
|
strings = []
|
|
for se in g.get("sections") or []:
|
|
strings += [se.get("title") or "", se.get("intro") or ""] + list(se.get("steps") or [])
|
|
for qa in g.get("faq") or []:
|
|
strings += [qa.get("q") or "", qa.get("a") or ""]
|
|
for s in strings:
|
|
for m in LABEL.finditer(s):
|
|
lab = m.group(1).strip()
|
|
if (lang, lab) not in out:
|
|
out.append((lang, lab))
|
|
return out
|
|
|
|
|
|
def files_of(corpus):
|
|
if os.path.isfile(corpus):
|
|
yield corpus
|
|
return
|
|
for dp, dn, fn in os.walk(corpus):
|
|
dn[:] = [d for d in dn if d not in (".git", "node_modules", "test", "tests", "__tests__")]
|
|
for f in fn:
|
|
if f.endswith(TEXT_EXT):
|
|
yield os.path.join(dp, f)
|
|
|
|
|
|
def find(label, corpora, cache):
|
|
e = re.escape(label)
|
|
pats = [re.compile(p) for p in (
|
|
r'QBT_TR\(%s\)QBT_TR' % e,
|
|
r'"%s"' % e, r"'%s'" % e, r'`%s`' % e,
|
|
r'>\s*%s\s*<' % e,
|
|
r'msgstr "%s"' % e, r'<translation>%s</translation>' % e, r'<source>%s</source>' % e,
|
|
r'(?m)^\s*[\w.\-]+\s*=\s*%s\s*$' % e, # .properties / .ini value
|
|
r'(?m)^\s*"?[\w.\-]+"?\s*:\s*%s\s*$' % e, # YAML value, unquoted
|
|
)]
|
|
for c in corpora:
|
|
for path in files_of(c):
|
|
if path not in cache:
|
|
try:
|
|
cache[path] = io.open(path, encoding="utf-8", errors="replace").read()
|
|
except OSError:
|
|
cache[path] = ""
|
|
text = cache[path]
|
|
if label not in text:
|
|
continue
|
|
for p in pats:
|
|
m = p.search(text)
|
|
if m:
|
|
line = text.count("\n", 0, m.start()) + 1
|
|
return "%s:%d" % (path, line)
|
|
return None
|
|
|
|
|
|
def main(argv):
|
|
draft = json.load(io.open(argv[1], encoding="utf-8"))
|
|
out_path, corpora = argv[2], argv[3:]
|
|
cache, lines, missing = {}, [], 0
|
|
labs = labels_of(draft)
|
|
lines.append("app: %s checked against: %s" % (draft.get("app"), draft.get("checked_version")))
|
|
lines.append("corpora: %s" % " ".join(corpora))
|
|
lines.append("labels: %d (Hungarian and English guide strings, every „…” occurrence)" % len(labs))
|
|
for lang, lab in labs:
|
|
hit = find(lab, corpora, cache)
|
|
if hit:
|
|
lines.append("OK [%s] „%s” %s" % (lang, lab, hit))
|
|
else:
|
|
missing += 1
|
|
lines.append("MISSING [%s] „%s”" % (lang, lab))
|
|
lines.append("RESULT: %s — %d label(s), %d missing" % ("ALL FOUND" if not missing else "NOT ALL FOUND",
|
|
len(labs), missing))
|
|
with io.open(out_path, "w", encoding="utf-8") as fh:
|
|
fh.write("\n".join(lines) + "\n")
|
|
print(lines[-1])
|
|
return 1 if missing else 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main(sys.argv))
|