Skip to content

Commit e2379c5

Browse files
committed
Update check_markup script
1 parent 9d088b5 commit e2379c5

1 file changed

Lines changed: 44 additions & 20 deletions

File tree

scripts/check_markup.py

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@
22
"""
33
scripts/check_markup.py
44
5-
Verify that Sphinx roles, inline literals, and format placeholders match
6-
exactly between the English msgid and the Persian msgstr — nothing missing,
7-
and nothing extra. Catches both the common review slip (translating or
8-
dropping `:class:`int``-style markup, ``code`` spans, %s/{0} placeholders,
9-
|substitution| refs) and the subtler mistake of adding a reference that
10-
doesn't exist in the original (which Sphinx's own build treats as a hard
11-
error under -W, e.g. "inconsistent term references in translated message").
5+
Verify Sphinx markup consistency between msgid and msgstr:
6+
- Roles like :term:`text <target>` — the *target* (or the whole role, if
7+
it has no explicit target) must match; the display text is expected to
8+
be translated, matching Sphinx's own translation convention.
9+
- Literal/code spans (``...``), substitution refs (|...|), and %s/{name}
10+
placeholders — these must match verbatim, since they're not prose.
1211
1312
Requires: pip install polib
1413
@@ -24,38 +23,63 @@
2423

2524
import polib
2625

27-
PATTERNS = [
28-
("sphinx role", re.compile(r":(?:\w+:)?[\w.-]+:`.*?`")),
26+
ROLE_PATTERN = re.compile(r":(?:\w+:)?[\w.-]+:`([^`]+)`")
27+
TARGET_PATTERN = re.compile(r"^(.*)\s<([^<>]+)>$")
28+
29+
LITERAL_PATTERNS = [
2930
("literal/code span", re.compile(r"``.*?``")),
3031
("substitution ref", re.compile(r"\|[\w.-]+\|")),
3132
("percent placeholder", re.compile(r"%\(\w+\)[a-zA-Z]|%[a-zA-Z]")),
3233
("brace placeholder", re.compile(r"\{[^{}\s]*\}")),
3334
]
3435

3536

37+
def extract_role_targets(text: str):
38+
"""For each Sphinx role, return its target: the <target> anchor if
39+
present, otherwise the role's full display text (which IS the target
40+
when there's no explicit anchor)."""
41+
targets = []
42+
for body in ROLE_PATTERN.findall(text):
43+
m = TARGET_PATTERN.match(body)
44+
targets.append(m.group(2).strip() if m else body.strip())
45+
return targets
46+
47+
3648
def check_file(path: Path) -> int:
3749
problems = 0
3850
po = polib.pofile(str(path))
3951
for entry in po:
4052
if entry.obsolete or not entry.msgid or not entry.msgstr:
4153
continue # obsolete entry, header, or still untranslated
42-
for label, pattern in PATTERNS:
54+
55+
findings = []
56+
57+
expected_targets = Counter(extract_role_targets(entry.msgid))
58+
found_targets = Counter(extract_role_targets(entry.msgstr))
59+
if expected_targets != found_targets:
60+
missing = list((expected_targets - found_targets).elements())
61+
extra = list((found_targets - expected_targets).elements())
62+
if missing:
63+
findings.append(f"missing role target(s): {missing}")
64+
if extra:
65+
findings.append(f"role target(s) not in source: {extra}")
66+
67+
for label, pattern in LITERAL_PATTERNS:
4368
expected = Counter(pattern.findall(entry.msgid))
4469
found = Counter(pattern.findall(entry.msgstr))
45-
if expected == found:
46-
continue
70+
if expected != found:
71+
missing = list((expected - found).elements())
72+
extra = list((found - expected).elements())
73+
if missing:
74+
findings.append(f"missing {label}: {missing}")
75+
if extra:
76+
findings.append(f"extra {label} not in source: {extra}")
4777

48-
missing = list((expected - found).elements())
49-
extra = list((found - expected).elements())
78+
if findings:
5079
problems += 1
5180
loc = f" ({entry.occurrences[0][0]}:{entry.occurrences[0][1]})" if entry.occurrences else ""
5281
tag = " [fuzzy]" if entry.fuzzy else ""
53-
parts = []
54-
if missing:
55-
parts.append(f"missing {label}: {missing}")
56-
if extra:
57-
parts.append(f"extra {label} not in source: {extra}")
58-
print(f"{path}{loc}{tag}: {'; '.join(parts)}")
82+
print(f"{path}{loc}{tag}: {'; '.join(findings)}")
5983
print(f" msgid : {entry.msgid[:100]}")
6084
print(f" msgstr: {entry.msgstr[:100]}")
6185
return problems

0 commit comments

Comments
 (0)