Add the Danish and Bahasa Malaysia wiki pages
Validate workflows / validate (pull_request) Successful in 15s

Draft ten translations (Home, Onboarding, Credentials, Troubleshooting,
Capabilities in da and ms) with the source hash header from ADR-0001, a
language switcher on every page and a term list. build_wiki.py --check now
reports missing or stale translations, and tests assert that translations
follow the structure of the English page.

The drafts await review by a native reader (MIL-005 task 5).

Task: MIL-005#4
This commit is contained in:
2026-10-10 09:24:41 +08:00
parent 0ddef2b695
commit 081c6b58f7
18 changed files with 885 additions and 2 deletions
+68 -2
View File
@@ -10,6 +10,7 @@ Usage:
"""
import argparse
import hashlib
import posixpath
import re
import sys
@@ -45,6 +46,13 @@ PAGES: tuple[Page, ...] = (
Page("Capabilities", "README.md", ("Capabilities",)),
)
# Translations (ADR-0001): `<Page>.<code>` written by hand in wiki/.
LANGUAGES: tuple[tuple[str, str], ...] = (("da", "Dansk"), ("ms", "Bahasa Melayu"))
HASH_LENGTH = 12
_TRANSLATION_HEADER = re.compile(
r"^<!-- translation of (?P<page>\S+)\.md, source sha256 (?P<hash>[0-9a-f]+) -->$"
)
# The page a link to a source file should point to. The README is the home page.
_PAGE_FOR_SOURCE = {
"README.md": "Home",
@@ -124,6 +132,22 @@ def split_h2_sections(text: str) -> tuple[list[str], dict[str, list[str]]]:
return preamble, sections
def language_switcher(page_name: str, current: str | None = None) -> str:
"""Return the line that links a page to its other languages.
``current`` is a language code, or None for the English page.
"""
items = ["English" if current is None else f"[English]({page_name})"]
for code, label in LANGUAGES:
items.append(label if code == current else f"[{label}]({page_name}.{code})")
return " | ".join(items)
def source_hash(content: bytes) -> str:
"""Return the short content hash a translation records for its source."""
return hashlib.sha256(content).hexdigest()[:HASH_LENGTH]
def render_page(page: Page, repo_root: Path = REPO_ROOT) -> str:
"""Return the full Markdown of one wiki page."""
source_path = repo_root / page.source
@@ -150,6 +174,7 @@ def render_page(page: Page, repo_root: Path = REPO_ROOT) -> str:
f"> This page is generated from [`{page.source}`]"
f"({SOURCE_BASE_URL}/{page.source}). Changes made here are overwritten "
"the next time the wiki is published.\n\n"
f"{language_switcher(page.name)}\n\n"
)
body = convert_markdown("\n".join(body_lines), page.source).strip("\n")
return banner + body + "\n"
@@ -185,18 +210,59 @@ def check_pages(wiki_dir: Path = WIKI_DIR, repo_root: Path = REPO_ROOT) -> list[
return problems
def check_translations(
wiki_dir: Path = WIKI_DIR, repo_root: Path = REPO_ROOT
) -> list[str]:
"""Return a message for every translation that is missing or stale.
A translation is stale when the hash it records no longer matches the
English page it follows (the English page changed after it was written).
"""
problems: list[str] = []
english = build_all(repo_root)
for page in PAGES:
current = source_hash(english[f"{page.name}.md"].encode("utf-8"))
for code, _label in LANGUAGES:
name = f"{page.name}.{code}.md"
path = wiki_dir / name
if not path.exists():
problems.append(f"{name}: missing")
continue
lines = path.read_text(encoding="utf-8").splitlines()
match = _TRANSLATION_HEADER.match(lines[0]) if lines else None
if match is None or match.group("page") != page.name:
problems.append(f"{name}: first line is not the translation header")
elif match.group("hash") != current:
problems.append(
f"{name}: stale, follows {match.group('hash')} but "
f"{page.name}.md is {current}"
)
return problems
def main(argv: list[str] | None = None) -> int:
"""Run the generator; return the process exit code."""
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument(
"--check",
action="store_true",
help="report pages that differ from their sources and exit 1",
help="report pages that differ from their sources or stale translations, exit 1",
)
parser.add_argument(
"--hash",
metavar="PAGE",
help="print the source hash a translation of PAGE must record",
)
args = parser.parse_args(argv)
try:
if args.hash:
content = build_all().get(f"{args.hash}.md")
if content is None:
raise WikiBuildError(f"unknown page '{args.hash}'")
print(source_hash(content.encode("utf-8")))
return 0
if args.check:
problems = check_pages()
problems = check_pages() + check_translations()
for problem in problems:
print(f"ERROR: wiki/{problem}", file=sys.stderr)
if problems: