From 10345650002af77e7943ef0f7e49881844ed7a64 Mon Sep 17 00:00:00 2001 From: Andrei Solodovnikov Date: Wed, 6 May 2026 12:53:35 +0300 Subject: [PATCH] =?UTF-8?q?WIP:=20=D1=81=D0=B1=D0=BE=D1=80=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=B0=D0=BD=D0=B3=D0=BB=D0=B8=D0=B9=D1=81=D0=BA=D0=BE=D0=B9=20?= =?UTF-8?q?=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D0=B8=20mdbook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/book.toml | 4 +- .github/book_en.toml | 12 +++ .github/prepare.sh | 10 +- .github/theme/language-switcher.css | 44 ++++++++ .github/theme/language-switcher.js | 149 ++++++++++++++++++++++++++++ .github/workflows/mdbook.yml | 83 ++++++++++++---- 6 files changed, 279 insertions(+), 23 deletions(-) create mode 100644 .github/book_en.toml create mode 100644 .github/theme/language-switcher.css create mode 100644 .github/theme/language-switcher.js diff --git a/.github/book.toml b/.github/book.toml index aa947a2..6442d29 100644 --- a/.github/book.toml +++ b/.github/book.toml @@ -1,10 +1,12 @@ [book] authors = ["Andrei Solodovnikov, Mikhail Popov"] language = "ru" -multilingual = true src = "./" title = "Архитектуры процессорных систем" [output.html] git-repository-url = "https://github.com/MPSU/APS" git-repository-icon = "fa-github" +site-url = "/APS/" +additional-js = ["theme/language-switcher.js"] +additional-css = ["theme/language-switcher.css"] diff --git a/.github/book_en.toml b/.github/book_en.toml new file mode 100644 index 0000000..f07f957 --- /dev/null +++ b/.github/book_en.toml @@ -0,0 +1,12 @@ +[book] +authors = ["Andrei Solodovnikov, Mikhail Popov"] +language = "en" +src = "./" +title = "Processor System Architectures" + +[output.html] +git-repository-url = "https://github.com/MPSU/APS" +git-repository-icon = "fa-github" +site-url = "/APS/en/" +additional-js = ["theme/language-switcher.js"] +additional-css = ["theme/language-switcher.css"] diff --git a/.github/prepare.sh b/.github/prepare.sh index 52f152a..bb3d1bf 100644 --- a/.github/prepare.sh +++ b/.github/prepare.sh @@ -1,4 +1,10 @@ #!/bin/bash +set -e -mkdir src -cp .github/book.toml .github/*.md ./ \ No newline at end of file +mkdir -p src +cp .github/book.toml .github/*.md ./ + +# Copy custom theme assets (language switcher, etc.) +if [ -d ".github/theme" ]; then + cp -r .github/theme ./ +fi diff --git a/.github/theme/language-switcher.css b/.github/theme/language-switcher.css new file mode 100644 index 0000000..bbfb64b --- /dev/null +++ b/.github/theme/language-switcher.css @@ -0,0 +1,44 @@ +/* ── Language Switcher ───────────────────────────────────────────────────── */ + +#language-switcher { + display: inline-flex; + align-items: center; + gap: 4px; + + background: none; + border: 1px solid var(--icons); + border-radius: 4px; + color: var(--icons); + cursor: pointer; + font-size: 0.82em; + font-weight: 600; + line-height: 1; + padding: 3px 8px; + + /* sit nicely alongside the existing icon buttons */ + margin-right: 4px; + vertical-align: middle; + + transition: color 0.15s, border-color 0.15s, background 0.15s; +} + +#language-switcher:hover, +#language-switcher:focus-visible { + color: var(--icons-hover); + border-color: var(--icons-hover); + background-color: rgba(128, 128, 128, 0.08); + outline: none; +} + +/* Keep the flag emoji from being shrunk by the parent flex layout */ +#language-switcher span[aria-hidden] { + font-size: 1.1em; + line-height: 1; +} + +/* Hide text label on very small screens, show only flag */ +@media (max-width: 420px) { + #language-switcher .lang-label { + display: none; + } +} diff --git a/.github/theme/language-switcher.js b/.github/theme/language-switcher.js new file mode 100644 index 0000000..2175ccb --- /dev/null +++ b/.github/theme/language-switcher.js @@ -0,0 +1,149 @@ +(function () { + "use strict"; + + const STORAGE_KEY = "book-language"; + + // --------------------------------- + // Current language from URL + // --------------------------------- + + function currentLang() { + return /\/en\//.test(window.location.pathname) ? "en" : "ru"; + } + + // --------------------------------- + // Browser language + // --------------------------------- + + function preferredLang() { + const langs = navigator.languages || [ + navigator.language || "en", + ]; + + const isRussian = langs.some((lang) => + lang.toLowerCase().startsWith("ru") + ); + + return isRussian ? "ru" : "en"; + } + + // --------------------------------- + // URL conversion + // --------------------------------- + + function pathForLang(lang) { + const path = window.location.pathname; + + if (lang === "en") { + if (/\/en\//.test(path)) { + return path; + } + + // /APS/foo → /APS/en/foo + return path.replace(/^(\/[^\/]+\/)/, "$1en/"); + } + + // lang === "ru" + + // /APS/en/foo → /APS/foo + return path.replace("/en/", "/"); + } + + // --------------------------------- + // Switch language manually + // --------------------------------- + + function switchLanguage() { + const newLang = currentLang() === "ru" ? "en" : "ru"; + + // Save explicit user choice + localStorage.setItem(STORAGE_KEY, newLang); + + window.location.href = + pathForLang(newLang) + + window.location.search + + window.location.hash; + } + + // --------------------------------- + // Automatic language selection + // --------------------------------- + + function autoSelectLanguage() { + const current = currentLang(); + + // 1. User preference has priority + let desired = localStorage.getItem(STORAGE_KEY); + + // 2. Otherwise use browser language + if (!desired) { + desired = preferredLang(); + } + + // Already correct + if (desired === current) { + return; + } + + // Redirect + window.location.replace( + pathForLang(desired) + + window.location.search + + window.location.hash + ); + } + + // --------------------------------- + // UI + // --------------------------------- + + function addSwitcher() { + const lang = currentLang(); + + const btn = document.createElement("button"); + btn.id = "language-switcher"; + + btn.setAttribute( + "title", + lang === "ru" + ? "Switch to English" + : "Переключить на русский" + ); + + btn.setAttribute("aria-label", btn.getAttribute("title")); + + const icon = document.createElement("span"); + icon.setAttribute("aria-hidden", "true"); + icon.textContent = lang === "ru" ? "🇬🇧" : "🇷🇺"; + + const label = document.createElement("span"); + label.className = "lang-label"; + label.textContent = lang === "ru" ? "EN" : "RU"; + + btn.appendChild(icon); + btn.appendChild(label); + + btn.addEventListener("click", switchLanguage); + + const target = document.querySelector(".right-buttons"); + + if (target) { + target.insertBefore(btn, target.firstChild); + } + } + + // --------------------------------- + // Init + // --------------------------------- + + function init() { + autoSelectLanguage(); + addSwitcher(); + } + + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", init); + } else { + init(); + } +})(); \ No newline at end of file diff --git a/.github/workflows/mdbook.yml b/.github/workflows/mdbook.yml index 83d6dd8..ad7fc9c 100644 --- a/.github/workflows/mdbook.yml +++ b/.github/workflows/mdbook.yml @@ -1,59 +1,102 @@ -# Sample workflow for building and deploying a mdBook site to GitHub Pages +# Builds the Russian (master) and English (english_version) editions of the +# mdBook and deploys them as one GitHub Pages site: # -# To get started with mdBook see: https://rust-lang.github.io/mdBook/index.html +# /APS/ ← Russian +# /APS/en/ ← English # name: Deploy mdBook site to Pages on: - # Runs on pushes targeting the default branch push: - branches: ["master"] - - # Allows you to run this workflow manually from the Actions tab + branches: ["master", "english_version"] workflow_dispatch: -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages permissions: contents: read - pages: write + pages: write id-token: write -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. -# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. concurrency: group: "pages" cancel-in-progress: false jobs: - # Build job build: runs-on: ubuntu-latest env: MDBOOK_VERSION: 0.4.36 + steps: - - uses: actions/checkout@v4 + # ── 1. Checkout master (Russian) ────────────────────────────────────── + - name: Checkout master (Russian) + uses: actions/checkout@v4 + # checks out into $GITHUB_WORKSPACE (i.e. the working directory) + + # ── 2. Install mdBook ───────────────────────────────────────────────── - name: Install mdBook + run: | + curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh -s -- -y + source "$HOME/.cargo/env" + cargo install --version "${MDBOOK_VERSION}" mdbook + + # ── 3. Build Russian version ────────────────────────────────────────── + - name: Prepare Russian sources run: | chmod +x .github/prepare.sh .github/prepare.sh - curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf -y | sh - rustup update - cargo install --version ${MDBOOK_VERSION} mdbook + + - name: Build Russian (mdbook → ./book/) + run: | + source "$HOME/.cargo/env" + mdbook build + + # ── 4. Checkout English branch ──────────────────────────────────────── + - name: Checkout english_version branch + uses: actions/checkout@v4 + with: + ref: english_version + path: english_src # checked out next to the working directory + + # ── 5. Build English version ────────────────────────────────────────── + - name: Prepare English sources + run: | + cd english_src + + chmod +x .github/prepare.sh + .github/prepare.sh + + # Use the dedicated English book.toml (sets site-url = "/APS/en/") + cp .github/book_en.toml book.toml + + # Copy language-switcher theme files from the master checkout + cp -r ../.github/theme ./theme + + - name: Build English (mdbook → english_src/book/) + run: | + source "$HOME/.cargo/env" + cd english_src + mdbook build + + # ── 6. Merge artifacts ──────────────────────────────────────────────── + - name: Merge English build into Russian book/en/ + run: | + mkdir -p book/en + cp -r english_src/book/. book/en/ + + # ── 7. Deploy ───────────────────────────────────────────────────────── - name: Setup Pages id: pages uses: actions/configure-pages@v5 - - name: Build with mdBook - run: mdbook build - - name: Upload artifact + + - name: Upload Pages artifact uses: actions/upload-pages-artifact@v3 with: path: ./book - # Deployment job deploy: environment: name: github-pages - url: ${{ steps.deployment.outputs.page_url }} + url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest needs: build steps: