40-validation/validate-primary-language.py
#!/usr/bin/env python3
import argparse,json,re
from pathlib import Path
EN={"the","and","for","with","without","from","to","of","validation","overview","purpose","architecture"};DE={"der","die","das","und","für","mit","ohne","von","zu","zweck","validierung","architektur"}
def meta(text):
if not text.startswith("---"):return {}
p=text.split("---",2);r={}
if len(p)<3:return r
for line in p[1].splitlines():
k,s,v=line.partition(":")
if s:r[k.strip()]=v.strip().strip('"')
return r
def words(text):return re.findall(r"[A-Za-zÄÖÜäöüß]+",text.lower())
def validate(root,config):
allowed=[re.compile(x) for x in config.get("allowedHeadingPatterns",[])];failed=[];review=[];checked=0
for path in sorted(root.rglob("*.md")):
text=path.read_text(encoding="utf-8");rel=path.relative_to(root).as_posix();m=meta(text)
if m.get("language") not in (None,"de"):failed.append({"code":"PRIMARY_LANGUAGE_NOT_DE","file":rel,"language":m.get("language")})
for heading in re.findall(r"^#{1,6}\s+(.+?)\s*$",text,re.M):
checked+=1
if any(p.fullmatch(heading) for p in allowed):continue
tokens=words(heading);en=sum(x in EN for x in tokens);de=sum(x in DE for x in tokens)
if en>=2 and en>de:failed.append({"code":"ENGLISH_HEADING","file":rel,"heading":heading})
elif tokens and not any(c in heading.lower() for c in "äöüß") and en==de==0:review.append({"code":"AMBIGUOUS_HEADING_LANGUAGE","file":rel,"heading":heading})
status="FAILED" if failed else ("REVIEW_REQUIRED" if review else "PASSED")
return {"schemaVersion":"1.0","workItem":"SE-0030","status":status,"files":len(list(root.rglob('*.md'))),"headings":checked,"technicalIdsAllowed":True,"failed":failed,"reviewCases":review}
def main():
p=argparse.ArgumentParser();p.add_argument("root",type=Path);p.add_argument("--config",type=Path,required=True);p.add_argument("--output",type=Path);a=p.parse_args();r=validate(a.root,json.loads(a.config.read_text()));text=json.dumps(r,ensure_ascii=False,indent=2)+"\n"
if a.output:a.output.parent.mkdir(parents=True,exist_ok=True);a.output.write_text(text)
else:print(text,end="")
raise SystemExit(1 if r["status"]=="FAILED" else 0)
if __name__=="__main__":main()