Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 63 additions & 7 deletions utils/validate_translation.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
"""Validate the state of a course translation against the English source.

This reports, for a given language:

* a progress summary (how many sections are translated vs. the English total),
* sections referenced in the language's ``_toctree.yml`` whose ``.mdx`` file is
missing on disk -- these are hard errors that break the ``doc-builder`` build,
* sections that still need to be translated.

Example:

python utils/validate_translation.py --language fr
"""

import argparse
import os
from pathlib import Path
Expand All @@ -8,16 +22,41 @@


def load_sections(language: str):
"""Return the set of section paths declared in a language's ``_toctree.yml``.

A section may declare a single ``local`` path or framework-specific
``local_fw`` paths (one each for the PyTorch and TensorFlow variants). We
collect all of them so validation works regardless of which style a chapter
uses, and so framework-specific files are not silently ignored.
"""
toc = yaml.safe_load(
open(os.path.join(PATH_TO_COURSE / language, "_toctree.yml"), "r")
)
sections = []
for chapter in toc:
for section in chapter["sections"]:
sections.append(section["local"])
if "local" in section:
sections.append(section["local"])
if "local_fw" in section:
sections.extend(section["local_fw"].values())
return set(sections)


def find_missing_files(language: str, sections):
"""Return sections declared in ``_toctree.yml`` whose ``.mdx`` file is absent.

The course can only be built when every section referenced by the table of
contents has a matching file on disk, so a missing file is a hard error
rather than simply untranslated content.
"""
missing_files = []
for section in sorted(sections):
mdx_path = PATH_TO_COURSE / language / f"{section}.mdx"
if not mdx_path.exists():
missing_files.append(section)
return missing_files


if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--language", type=str, help="Translation language to validate")
Expand All @@ -26,14 +65,31 @@ def load_sections(language: str):
english_sections = load_sections("en")
translation_sections = load_sections(args.language)
missing_sections = sorted(english_sections.difference(translation_sections))
missing_files = find_missing_files(args.language, translation_sections)

if len(missing_sections) > 0:
print("Completed sesions:\n")
for section in sorted(translation_sections):
print(section)
total = len(english_sections)
completed = total - len(missing_sections)
percentage = (completed / total * 100) if total else 0
print(
f"πŸ“Š '{args.language}' translation progress: "
f"{completed}/{total} sections ({percentage:.1f}%)\n"
)

if missing_files:
print("❌ Sections listed in _toctree.yml but missing their .mdx file")
print(" (these break the course build):\n")
for section in missing_files:
print(f" - {section}.mdx")
print()

print("\nMissing sections:\n")
if missing_sections:
print("πŸ“ Sections not yet translated:\n")
for section in missing_sections:
print(section)
print(f" - {section}")
else:
print("βœ… No missing sections - translation complete!")

# Exit with a non-zero status when the table of contents references files
# that do not exist, so the script can double as a CI gate.
if missing_files:
raise SystemExit(1)
Loading