"""Fail the i18n build on an unescaped literal '%' in a catalog.

Why this exists: Jinja's newstyle gettext ALWAYS runs the translated string
through %-formatting, even when the string takes no variables ("Always treat as
a format string… This requires escaping" — jinja2/ext.py). So a literal percent
does one of two things, and the quiet one is worse:

  "…businesses: 0% reverse charge…" % {}   ->  "…businesses: 0{}everse charge…"
  "…sujeto pasivo (0%)."           % {}   ->  ValueError, HTTP 500

The first shipped live on the English pricing page and nothing complained. A
percent in a translatable string must be written '%%' in BOTH the template and
every translation.

Usage: check-i18n-percent.py <catalog.po|.pot> [...]
"""
import re
import sys

from babel.messages.pofile import read_po

# Strip every legitimate token first — %(name)s, %s, %d and the escape %% —
# then any surviving '%' is a literal one that was never escaped. Doing it with a
# single negative lookahead does not work: in "0%%" the lookahead accepts the
# first '%' and then flags the second.
VALID = re.compile(r"%\([^)]*\)[sd]|%[sd]|%%")


def has_bare_percent(text: str) -> bool:
    return "%" in VALID.sub("", text)

problems = []
for path in sys.argv[1:]:
    with open(path, encoding="utf-8") as handle:
        catalog = read_po(handle)
    for message in catalog:
        if not message.id or not isinstance(message.id, str):
            continue
        for label, text in (("msgid", message.id), ("msgstr", message.string or "")):
            if has_bare_percent(text):
                problems.append((path, label, text[:100]))

for path, label, text in problems:
    print(f"  {path} [{label}] {text!r}")

if problems:
    print(f"\n{len(problems)} unescaped '%' found — write it as '%%' in the template "
          "AND in every translation, then re-run this script.")
    sys.exit(1)
