diff --git a/docs/book/build/build.py b/docs/book/build/build.py index 66bd8d2..ab49276 100644 --- a/docs/book/build/build.py +++ b/docs/book/build/build.py @@ -22,6 +22,8 @@ import yaml sys.path.insert(0, str(Path(__file__).resolve().parent)) +import os # noqa: E402 +import md # noqa: E402 (to set md.LANG for localized callout labels) from md import render_markdown # noqa: E402 from epub import EpubBuilder, Doc # noqa: E402 from pdf import render_pdf # noqa: E402 @@ -33,6 +35,9 @@ PDF_NAME = "firefly-rust-by-example.pdf" EPUB_NAME = "firefly-rust-by-example.epub" +# Localizable structural labels; overridden per-language via book.yaml `labels:`. +LABELS = {"chapter": "Chapter", "appendix": "Appendix", "contents": "Contents"} + _H1_RE = re.compile(r']*>.*?', re.DOTALL) @@ -87,7 +92,7 @@ def _items_from_manifest(cfg: dict) -> list[dict]: # not in the nav) precede it; the readable sections (preface, conventions, # the Rust primer) follow it and are listed *in* it. items.extend(f for f in front_items if not f["in_nav"]) - items.append({"kind": "toc", "id": "toc", "title": "Contents"}) + items.append({"kind": "toc", "id": "toc", "title": LABELS["contents"]}) items.extend(f for f in front_items if f["in_nav"]) # 3) parts @@ -121,10 +126,10 @@ def _chapter_head(num, raw_title: str, opener: str | None) -> str: if num in (None, ""): eyebrow = "" elif isinstance(num, str): - eyebrow = f'Appendix {escape(num)}' \ + eyebrow = f'{LABELS["appendix"]} {escape(num)}' \ if len(num) == 1 and num.isalpha() else f'{escape(num)}' else: - eyebrow = f'Chapter {num}' + eyebrow = f'{LABELS["chapter"]} {num}' op_html = "" if opener: p = BOOK / opener @@ -173,7 +178,7 @@ def _toc_html(items: list[dict], *, href_fmt: str) -> str: f'{escape(it["title"])}') if open_group: parts.append("") - return f'

Contents

{"".join(parts)}' + return f'

{escape(LABELS["contents"])}

{"".join(parts)}' def _divider_html(eyebrow: str, ptitle: str) -> str: @@ -210,7 +215,15 @@ def _divider_html(eyebrow: str, ptitle: str) -> str: def main() -> int: - cfg = yaml.safe_load((BOOK / "book.yaml").read_text()) + # The manifest (default book.yaml) can be overridden for a localized build, + # e.g. BOOK_CONFIG=book-es.yaml for the Spanish edition. + cfg_name = os.environ.get("BOOK_CONFIG", "book.yaml") + cfg = yaml.safe_load((BOOK / cfg_name).read_text()) + # Localize structural labels + callout labels for this edition's language. + md.LANG = cfg.get("language", "en") + LABELS.update(cfg.get("labels", {})) + pdf_name = cfg.get("pdf_name", PDF_NAME) + epub_name = cfg.get("epub_name", EPUB_NAME) css_text = [(THEME / "tokens.css").read_text(), (THEME / "pygments.css").read_text(), (THEME / "book.css").read_text()] @@ -252,7 +265,7 @@ def main() -> int: epub.add_doc(Doc(id=it["id"], title=it["title"], xhtml_body=body, in_nav=True, kind="chapter", part=it.get("part"), num=it.get("num"))) - epub.build(DIST / EPUB_NAME) + epub.build(DIST / epub_name) # ---- PDF (single concatenated document) ---- if do_pdf: @@ -277,7 +290,7 @@ def main() -> int: render_pdf(full, base_url=BOOK, css_paths=[THEME / "tokens.css", THEME / "pygments.css", THEME / "book.css", THEME / "print.css"], - out=DIST / PDF_NAME) + out=DIST / pdf_name) nch = sum(1 for it in items if it["kind"] == "chapter") nfr = sum(1 for it in items if it["kind"] == "front") diff --git a/docs/book/build/md.py b/docs/book/build/md.py index 98b03d6..cc7479a 100644 --- a/docs/book/build/md.py +++ b/docs/book/build/md.py @@ -60,6 +60,13 @@ "note": "Note", "tip": "Tip", "warning": "Warning", "spring": "Spring parity", "reactor": "Reactor parity", } +# Spanish callout labels for the localized (ES) build, selected via LANG. +_CALLOUT_LABEL_ES = { + "note": "Nota", "tip": "Consejo", "warning": "Advertencia", + "spring": "Equivalencia con Spring", "reactor": "Equivalencia con Reactor", +} +# The build sets this to "es" to localize callout labels; default is English. +LANG = "en" # Professional inline SVG icons injected into callout titles (no emoji). _ADM_ICON = { @@ -167,7 +174,7 @@ def _blockquote(self, block: list[str]) -> str: tail = "\n".join(block[1:]).strip() body_md = (first_rest + ("\n" + tail if tail else "")).strip() inner = _inline_md(body_md) - label = _CALLOUT_LABEL[css] + label = (_CALLOUT_LABEL_ES if LANG == "es" else _CALLOUT_LABEL)[css] icon = _ADM_ICON.get(css, "") return (f'
' f'

{icon}{html.escape(label)}

'