#!/usr/bin/env python3 """Build static localized HTML for 428th.com (ru / en / ua).""" from __future__ import annotations import json import re from pathlib import Path ROOT = Path(__file__).resolve().parent.parent SRC = ROOT / "src" DIST = ROOT SITE = "https://428th.com" LOCALES = [ { "id": "ru", "prefix": "", "html_lang": "ru", "hreflang": "ru", "dict": "ru", "og_locale": "ru_RU", "label": "RU", }, { "id": "en", "prefix": "/en", "html_lang": "en", "hreflang": "en", "dict": "en", "og_locale": "en_US", "label": "EN", }, { "id": "ua", "prefix": "/ua", "html_lang": "uk", "hreflang": "uk", "dict": "uk", "og_locale": "uk_UA", "label": "UA", }, ] PAGES = [ { "id": "home", "template": "index.html", "title_key": "meta.title", "desc_key": "meta.description", "path_for": lambda prefix: f"{prefix}/" if prefix else "/", "out_for": lambda prefix: ( DIST / "index.html" if not prefix else DIST / prefix.lstrip("/") / "index.html" ), "depth_for": lambda prefix: 0 if not prefix else 1, }, { "id": "terminal", "template": "terminal.html", "title_key": "terminal.meta.title", "desc_key": "terminal.meta.description", "path_for": lambda prefix: f"{prefix}/terminal/" if prefix else "/terminal/", "out_for": lambda prefix: ( DIST / "terminal" / "index.html" if not prefix else DIST / prefix.lstrip("/") / "terminal" / "index.html" ), "depth_for": lambda prefix: 1 if not prefix else 2, }, ] def asset_prefix(_depth: int) -> str: """Root-absolute assets — avoids FOUC on /terminal vs /terminal/ and deep locales.""" return "/" def t(translations: dict, dict_key: str, key: str) -> str: value = translations.get(dict_key, {}).get(key) if value is None: value = translations.get("ru", {}).get(key, "") return value def localize_urls(html: str, prefix: str) -> str: if not prefix: return html html = re.sub( r'href="/terminal/?"', f'href="{prefix}/terminal/"', html, ) html = re.sub( r'href="/#([^"]*)"', lambda m: f'href="{prefix}/#{m.group(1)}"', html, ) return html def fill_i18n(html: str, translations: dict, dict_key: str) -> str: def repl_el(match: re.Match) -> str: open_tag, key, close = match.group(1), match.group(2), match.group(4) open_tag = re.sub(r'\sdata-i18n="[^"]+"', "", open_tag) return f"{open_tag}{t(translations, dict_key, key)}{close}" html = re.sub( r'(<\w+\b[^>]*\sdata-i18n="([^"]+)"[^>]*>)(.*?)()', repl_el, html, flags=re.DOTALL, ) def repl_placeholder(match: re.Match) -> str: before, key, after = match.group(1), match.group(2), match.group(3) value = esc_attr(t(translations, dict_key, key)) return f'{before}placeholder="{value}"{after}' html = re.sub( r'(\s)data-i18n-placeholder="([^"]+)"([^>]*>)', repl_placeholder, html, ) def repl_aria(match: re.Match) -> str: before, key, after = match.group(1), match.group(2), match.group(3) value = esc_attr(t(translations, dict_key, key)) after = re.sub(r'\saria-label="[^"]*"', "", after) before = re.sub(r'\saria-label="[^"]*"', "", before) return f'{before} aria-label="{value}"{after}' html = re.sub( r'(<\w+\b[^>]*)\sdata-i18n-aria="([^"]+)"([^>]*>)', repl_aria, html, ) return html def page_url(locale: dict, page: dict) -> str: return SITE + page["path_for"](locale["prefix"]) def alternate_links(page: dict) -> str: lines = [] for loc in LOCALES: lines.append( f'' ) lines.append( f'' ) return "\n".join(lines) def lang_switch(locale: dict, page: dict) -> str: parts = [] order = ["en", "ua", "ru"] by_id = {loc["id"]: loc for loc in LOCALES} for i, lid in enumerate(order): loc = by_id[lid] href = page["path_for"](loc["prefix"]) active = ' class="active"' if loc["id"] == locale["id"] else "" aria = ' aria-current="page"' if loc["id"] == locale["id"] else "" parts.append( f'{loc["label"]}' ) if i < len(order) - 1: parts.append(" //") return "\n ".join(parts) def esc_attr(value: str) -> str: return ( value.replace("&", "&") .replace('"', """) .replace("<", "<") .replace(">", ">") ) def head_meta(locale: dict, page: dict, translations: dict) -> str: title = t(translations, locale["dict"], page["title_key"]) desc = t(translations, locale["dict"], page["desc_key"]) url = page_url(locale, page) return f"""{title} {alternate_links(page)} """ LANG_REDIRECT_SCRIPT = r"""""" THEME_SCRIPT = """""" def home_href(locale: dict) -> str: return f"{locale['prefix']}/" if locale["prefix"] else "/" def terminal_href(locale: dict) -> str: return f"{locale['prefix']}/terminal/" if locale["prefix"] else "/terminal/" def build_page(locale: dict, page: dict, translations: dict, template: str) -> str: prefix = locale["prefix"] depth = page["depth_for"](prefix) assets = asset_prefix(depth) html = template html = html.replace("{{HTML_LANG}}", locale["html_lang"]) html = html.replace("{{PAGE_LANG}}", locale["id"]) html = html.replace("{{HEAD_META}}", head_meta(locale, page, translations)) html = html.replace("{{THEME_SCRIPT}}", THEME_SCRIPT) html = html.replace("{{LANG_REDIRECT_SCRIPT}}", LANG_REDIRECT_SCRIPT) html = html.replace("{{HOME_HREF}}", home_href(locale)) html = html.replace("{{TERMINAL_HREF}}", terminal_href(locale)) html = html.replace("{{ASSET_PREFIX}}", assets) html = html.replace( "{{FORM_STATUS_LOADING}}", esc_attr(t(translations, locale["dict"], "form.statusLoading")), ) html = html.replace( "{{FORM_STATUS_SUCCESS}}", esc_attr(t(translations, locale["dict"], "form.statusSuccess")), ) html = fill_i18n(html, translations, locale["dict"]) html = localize_urls(html, prefix) # After localize_urls so RU /terminal/ in the switch is not rewritten to /en/terminal/ html = html.replace("{{LANG_SWITCH}}", lang_switch(locale, page)) return html def write_sitemap() -> None: urls = [] for page in PAGES: for loc in LOCALES: loc_url = page_url(loc, page) if page["id"] == "home" and loc["id"] == "ru": priority = "1.0" elif page["id"] == "home" or loc["id"] == "ru": priority = "0.9" else: priority = "0.8" urls.append( f""" {loc_url} weekly {priority} """ ) content = ( '\n' '\n' + "\n".join(urls) + "\n\n" ) (DIST / "sitemap.xml").write_text(content, encoding="utf-8") def main() -> None: translations = json.loads((SRC / "translations.json").read_text(encoding="utf-8")) templates = { "index.html": (SRC / "index.html").read_text(encoding="utf-8"), "terminal.html": (SRC / "terminal.html").read_text(encoding="utf-8"), } for page in PAGES: for loc in LOCALES: out = page["out_for"](loc["prefix"]) out.parent.mkdir(parents=True, exist_ok=True) html = build_page(loc, page, translations, templates[page["template"]]) out.write_text(html, encoding="utf-8") print(f"wrote {out.relative_to(DIST)}") write_sitemap() print("wrote sitemap.xml") print("build ok") if __name__ == "__main__": main()