#!/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="([^"]+)"[^>]*>)(.*?)(\w+>)', 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"""