diff --git a/scripts/generate_site.py b/scripts/generate_site.py
index 772824f1..68905eb1 100644
--- a/scripts/generate_site.py
+++ b/scripts/generate_site.py
@@ -1707,7 +1707,7 @@ def _render_platform_systems(
anchor = sys_id.replace(" ", "-")
if use_collapsible:
- status_tag = "OK" if ok_count == total else f"{total - ok_count} issues"
+ status_tag = "OK" if ok_count == total else _count(total - ok_count, "issue")
lines.append(f'')
lines.append(f'??? note "{sys_id} ({ok_count}/{total} - {status_tag})"')
lines.append("")
@@ -3052,7 +3052,7 @@ def _emulator_file_summary(
held = f"{in_repo_count} in repo, {missing_count} missing"
if unsourceable_count:
held += f", {unsourceable_count} unsourceable"
- total = f"**{len(slots)} files**"
+ total = f"**{_count(len(slots), 'file')}**"
if len(slots) != len(files):
total += f" ({len(files)} accepted versions)"
parts = [
@@ -3344,7 +3344,7 @@ def _render_problem_files(
if missing_files:
lines.extend([
f'### Missing '
- f"{len(missing_files)} files",
+ f"{_count(len(missing_files), 'file')}",
"",
"| File | Required | Platforms |",
"|------|----------|-----------|",
@@ -3359,7 +3359,7 @@ def _render_problem_files(
if untested_files:
lines.extend([
f'### Untested '
- f"{len(untested_files)} files",
+ f"{_count(len(untested_files), 'file')}",
"",
"Present but hash not verified.",
"",
@@ -3375,7 +3375,7 @@ def _render_problem_files(
if mismatch_files:
lines.extend([
f'### Hash Mismatch '
- f"{len(mismatch_files)} files",
+ f"{_count(len(mismatch_files), 'file')}",
"",
"Platform says OK but emulator validation disagrees.",
"",
@@ -3792,11 +3792,11 @@ def generate_gap_analysis(
if src_missing_details:
req_src = [m for m in src_missing_details if m["required"]]
lines.extend([
- f"### Files to Source ({len(src_missing_details)} files, "
+ f"### Files to Source ({_count(len(src_missing_details), 'file')}, "
f"{len(req_src)} required"
+ (
- f"; {entries} entries, a file declared with the same hash "
- "at several places is listed once)"
+ f", {_count(entries, 'entry', 'entries')}: "
+ "a file declared with the same hash at several places is listed once)"
if entries != len(src_missing_details) else ")"
),
"",
@@ -4932,6 +4932,7 @@ from sitesources import ( # noqa: E402,F401
)
from siterender import ( # noqa: E402,F401
_by_mode,
+ _count,
_render_yaml_value,
_slugify_anchor,
_system_page_map,
diff --git a/scripts/siterender.py b/scripts/siterender.py
index a1999d01..1cf06c88 100644
--- a/scripts/siterender.py
+++ b/scripts/siterender.py
@@ -30,6 +30,11 @@ def _code(value: object) -> str:
return "`" + str(value).replace("`", "'") + "`"
+def _count(number: int, noun: str, plural: str = "") -> str:
+ """A count and its noun, singular for one: the site printed "1 files" 234 times."""
+ return f"{number:,} {noun if number == 1 else plural or noun + 's'}"
+
+
def _escape_tags(text: str) -> str:
"""Show a placeholder such as ```` as it was written.
diff --git a/tests/test_prose_style.py b/tests/test_prose_style.py
index 84d750ab..29309ebc 100644
--- a/tests/test_prose_style.py
+++ b/tests/test_prose_style.py
@@ -27,6 +27,8 @@ GLYPHS = re.compile(
# backticks, which are blanked before the search.
CLAUSE_SEMICOLON = re.compile(r"[A-Za-z0-9)\]\"']; [A-Za-z(]")
BACKTICKS = re.compile(r"`[^`\n]*`")
+# "1 files", a count the generator printed without its singular.
+ONE_PLURAL = re.compile(r"(?", "", page.read_text(encoding="utf-8"), flags=re.S)
text = FENCED.sub("", text)
for number, line in enumerate(text.splitlines(), 1):
- if GLYPHS.search(line) or _semicolons(line):
+ if GLYPHS.search(line) or _semicolons(line) or ONE_PLURAL.search(line):
offenders.append(f"{page.relative_to(ROOT)}:{number}: {line.strip()[:100]}")
self.assertEqual(offenders, [], "\n".join(offenders[:40]))