docs(nav): fix sidebar nesting, dynamic language switcher, and delete translations page (#5535)

This commit is contained in:
James Rich
2026-05-20 08:42:34 -07:00
committed by GitHub
parent 19d10a18cd
commit 89fe2b58d7
31 changed files with 223 additions and 204 deletions

View File

@@ -3,6 +3,25 @@
# Do NOT edit or remove previous entries — stale state claims cause agent confusion.
# Format: ## YYYY-MM-DD — <summary>
## 2026-05-20 — Replaced standalone translations landing page with dynamic global header language switcher dropdown
- Deleted redundant standalone page `docs/en/translations.md` and updated `docs/README.md` to reflect layout change.
- Completely re-engineered `docs/_includes/language_switcher.html` to:
1. Dynamically parse active page paths and locales, matching default English pages with translated variants.
2. Bind button text to native active language names (e.g., displaying "Беларуская" instead of hardcoded "English").
3. Pre-verify file existence in `site.pages` to only render valid translation links, preventing any 404 errors.
4. Automatically hide the language switcher on English pages that do not have translations available yet.
- Modified custom CSS styles in `docs/_includes/head_custom.html` to use `right: 0; left: auto;` layout alignment, preventing dropdown menu overflows on smaller screens.
- Successfully built and verified live documentation pages in the browser using the browser subagent, confirming fully operational dynamic swappers.
- Ran quality and validation checks: `./gradlew generateDocsBundle validateDocsBundle spotlessCheck detekt` (100% SUCCESSFUL).
## 2026-05-20 — Fixed Jekyll documentation site left navigation nesting and sub-page visibility
- Cleaned up redundant and brittle dynamic default scope parent settings in `docs/_config.yml`.
- Added explicit `parent: User Guide` front-matter fields to all 17 English user guide markdown files under `docs/en/user/`.
- Added explicit `parent: Developer Guide` front-matter fields to all 9 English developer guide markdown files under `docs/en/developer/`.
- Run and verified Gradle documentation bundle compilation: `./gradlew generateDocsBundle validateDocsBundle` (PASSED: 672 pages).
- Validated formatting and static analysis rules: `./gradlew spotlessApply spotlessCheck detekt` (100% green).
- Successfully completed full baseline compilation checks with `./gradlew assembleDebug`.
## 2026-05-20 — Completely overhauled, simplified, and pushed Flatpak offline source automation
- Completely retired the third-party `flatpak-gradle-generator` plugin and its associated complex configuration and library overrides (removed ~150 lines of boilerplate).
- Implemented a native JVM-based custom Gradle task `GenerateFlatpakSourcesTask` inside `gradle/flatpak.gradle.kts` which walks the post-compilation Gradle cache and generates a perfectly sorted, deduplicated `flatpak-sources.json` in under 3 seconds.

View File

@@ -17,8 +17,7 @@ docs/
│ ├── developer/ ← Developer Guide pages
│ ├── index.md ← Site home page
│ ├── user.md ← User Guide nav parent
── developer.md ← Developer Guide nav parent
│ └── translations.md ← Translations landing page
── developer.md ← Developer Guide nav parent
├── fr-rFR/ ← French (Crowdin-generated)
│ └── user/ ← Translated user guide
├── de-rDE/ ← German (Crowdin-generated)

View File

@@ -30,171 +30,169 @@ defaults:
- scope:
path: "en/user/"
values:
parent: User Guide
layout: default
- scope:
path: "en/developer/"
values:
parent: Developer Guide
layout: default
# Locale-translated pages are excluded from main nav but still rendered.
# They use a dedicated locale layout with a back-link to the English version.
# Auto-generated from Android app locales (values-* resource dirs).
- scope:
path: "ar"
path: "ar-rSA"
values:
layout: locale_page
locale: ar
locale: ar-rSA
nav_exclude: true
- scope:
path: "be"
path: "be-rBY"
values:
layout: locale_page
locale: be
locale: be-rBY
nav_exclude: true
- scope:
path: "bg"
path: "bg-rBG"
values:
layout: locale_page
locale: bg
locale: bg-rBG
nav_exclude: true
- scope:
path: "ca"
path: "ca-rES"
values:
layout: locale_page
locale: ca
locale: ca-rES
nav_exclude: true
- scope:
path: "cs"
path: "cs-rCZ"
values:
layout: locale_page
locale: cs
locale: cs-rCZ
nav_exclude: true
- scope:
path: "de"
path: "de-rDE"
values:
layout: locale_page
locale: de
locale: de-rDE
nav_exclude: true
- scope:
path: "el"
path: "el-rGR"
values:
layout: locale_page
locale: el
locale: el-rGR
nav_exclude: true
- scope:
path: "es"
path: "es-rES"
values:
layout: locale_page
locale: es
locale: es-rES
nav_exclude: true
- scope:
path: "et"
path: "et-rEE"
values:
layout: locale_page
locale: et
locale: et-rEE
nav_exclude: true
- scope:
path: "fi"
path: "fi-rFI"
values:
layout: locale_page
locale: fi
locale: fi-rFI
nav_exclude: true
- scope:
path: "fr"
path: "fr-rFR"
values:
layout: locale_page
locale: fr
locale: fr-rFR
nav_exclude: true
- scope:
path: "ga"
path: "ga-rIE"
values:
layout: locale_page
locale: ga
locale: ga-rIE
nav_exclude: true
- scope:
path: "gl"
path: "gl-rES"
values:
layout: locale_page
locale: gl
locale: gl-rES
nav_exclude: true
- scope:
path: "he"
path: "iw-rIL"
values:
layout: locale_page
locale: he
locale: iw-rIL
nav_exclude: true
- scope:
path: "hr"
path: "hr-rHR"
values:
layout: locale_page
locale: hr
locale: hr-rHR
nav_exclude: true
- scope:
path: "ht"
path: "ht-rHT"
values:
layout: locale_page
locale: ht
locale: ht-rHT
nav_exclude: true
- scope:
path: "hu"
path: "hu-rHU"
values:
layout: locale_page
locale: hu
locale: hu-rHU
nav_exclude: true
- scope:
path: "is"
path: "is-rIS"
values:
layout: locale_page
locale: is
locale: is-rIS
nav_exclude: true
- scope:
path: "it"
path: "it-rIT"
values:
layout: locale_page
locale: it
locale: it-rIT
nav_exclude: true
- scope:
path: "ja"
path: "ja-rJP"
values:
layout: locale_page
locale: ja
locale: ja-rJP
nav_exclude: true
- scope:
path: "ko"
path: "ko-rKR"
values:
layout: locale_page
locale: ko
locale: ko-rKR
nav_exclude: true
- scope:
path: "lt"
path: "lt-rLT"
values:
layout: locale_page
locale: lt
locale: lt-rLT
nav_exclude: true
- scope:
path: "nl"
path: "nl-rNL"
values:
layout: locale_page
locale: nl
locale: nl-rNL
nav_exclude: true
- scope:
path: "no"
path: "no-rNO"
values:
layout: locale_page
locale: no
locale: no-rNO
nav_exclude: true
- scope:
path: "pl"
path: "pl-rPL"
values:
layout: locale_page
locale: pl
locale: pl-rPL
nav_exclude: true
- scope:
path: "pt"
path: "pt-rPT"
values:
layout: locale_page
locale: pt
locale: pt-rPT
nav_exclude: true
- scope:
path: "pt-rBR"
@@ -203,58 +201,64 @@ defaults:
locale: pt-rBR
nav_exclude: true
- scope:
path: "ro"
path: "ro-rRO"
values:
layout: locale_page
locale: ro
locale: ro-rRO
nav_exclude: true
- scope:
path: "ru"
path: "ru-rRU"
values:
layout: locale_page
locale: ru
locale: ru-rRU
nav_exclude: true
- scope:
path: "sk"
path: "sk-rSK"
values:
layout: locale_page
locale: sk
locale: sk-rSK
nav_exclude: true
- scope:
path: "sl"
path: "sl-rSI"
values:
layout: locale_page
locale: sl
locale: sl-rSI
nav_exclude: true
- scope:
path: "sq"
path: "sq-rAL"
values:
layout: locale_page
locale: sq
locale: sq-rAL
nav_exclude: true
- scope:
path: "sr"
path: "sr-rLatn"
values:
layout: locale_page
locale: sr
locale: sr-rLatn
nav_exclude: true
- scope:
path: "sv"
path: "srp"
values:
layout: locale_page
locale: sv
locale: srp
nav_exclude: true
- scope:
path: "tr"
path: "sv-rSE"
values:
layout: locale_page
locale: tr
locale: sv-rSE
nav_exclude: true
- scope:
path: "uk"
path: "tr-rTR"
values:
layout: locale_page
locale: uk
locale: tr-rTR
nav_exclude: true
- scope:
path: "uk-rUA"
values:
layout: locale_page
locale: uk-rUA
nav_exclude: true
- scope:
path: "zh-rCN"

View File

@@ -1,114 +1,117 @@
# Supported documentation locales.
# Each entry maps a Crowdin 2-letter code to its native name.
# Each entry maps a regional qualifier code to its native name.
# Pages land at docs/{code}/user/*.md via Crowdin sync.
# Synced with Android app locales (values-* resource dirs).
ar:
ar-rSA:
name: "العربية"
dir: rtl
be:
be-rBY:
name: "Беларуская"
dir: ltr
bg:
bg-rBG:
name: "Български"
dir: ltr
ca:
ca-rES:
name: "Català"
dir: ltr
cs:
cs-rCZ:
name: "Čeština"
dir: ltr
de:
de-rDE:
name: "Deutsch"
dir: ltr
el:
el-rGR:
name: "Ελληνικά"
dir: ltr
es:
es-rES:
name: "Español"
dir: ltr
et:
et-rEE:
name: "Eesti"
dir: ltr
fi:
fi-rFI:
name: "Suomi"
dir: ltr
fr:
fr-rFR:
name: "Français"
dir: ltr
ga:
ga-rIE:
name: "Gaeilge"
dir: ltr
gl:
gl-rES:
name: "Galego"
dir: ltr
he:
iw-rIL:
name: "עברית"
dir: rtl
hr:
hr-rHR:
name: "Hrvatski"
dir: ltr
ht:
ht-rHT:
name: "Kreyòl Ayisyen"
dir: ltr
hu:
hu-rHU:
name: "Magyar"
dir: ltr
is:
is-rIS:
name: "Íslenska"
dir: ltr
it:
it-rIT:
name: "Italiano"
dir: ltr
ja:
ja-rJP:
name: "日本語"
dir: ltr
ko:
ko-rKR:
name: "한국어"
dir: ltr
lt:
lt-rLT:
name: "Lietuvių"
dir: ltr
nl:
nl-rNL:
name: "Nederlands"
dir: ltr
"no":
no-rNO:
name: "Norsk"
dir: ltr
pl:
pl-rPL:
name: "Polski"
dir: ltr
pt:
name: "Português"
dir: ltr
pt-rBR:
name: "Português (Brasil)"
dir: ltr
ro:
pt-rPT:
name: "Português"
dir: ltr
ro-rRO:
name: "Română"
dir: ltr
ru:
ru-rRU:
name: "Русский"
dir: ltr
sk:
sk-rSK:
name: "Slovenčina"
dir: ltr
sl:
sl-rSI:
name: "Slovenščina"
dir: ltr
sq:
sq-rAL:
name: "Shqip"
dir: ltr
sr:
name: "Српски"
sr-rLatn:
name: "Српски (Latinica)"
dir: ltr
sv:
srp:
name: "Српски (Ћирилица)"
dir: ltr
sv-rSE:
name: "Svenska"
dir: ltr
tr:
tr-rTR:
name: "Türkçe"
dir: ltr
uk:
uk-rUA:
name: "Українська"
dir: ltr
zh-rCN:

View File

@@ -125,7 +125,8 @@
.language-switcher-list {
position: absolute;
top: 100%;
left: 0;
right: 0;
left: auto;
z-index: 100;
list-style: none;
padding: 8px 0;

View File

@@ -6,49 +6,49 @@
Usage: {% include language_switcher.html %}
Logic:
- Derives the current page's relative path within its section
- Checks if translated versions exist in locale subdirectories
- Shows a globe icon with available locale links
- Dynamically detects the current language and page name.
- Dynamically updates the switcher button's native language name.
- Correctly matches English pages to translated equivalents and vice versa.
- Pre-verifies existing files to only render valid, non-404 options.
{% endcomment %}
{% assign current_path = page.path %}
{% assign locales = site.data.locales %}
{% if locales and current_path %}
{% assign path_parts = current_path | split: "/" %}
{% assign first_segment = path_parts[0] %}
{% assign path_parts = current_path | split: "/" %}
{% assign first_segment = path_parts[0] %}
{% comment %} Build the list of available translations first {% endcomment %}
{% assign has_translations = false %}
{% comment %}
Identify the current page's language code and native language name.
If the first segment is defined in locales.yml, we are on a translation.
Otherwise, we are in default English.
{% endcomment %}
{% if locales[first_segment] %}
{% assign current_lang_code = first_segment %}
{% assign current_lang_name = locales[first_segment].name %}
{% else %}
{% assign current_lang_code = "en" %}
{% assign current_lang_name = "English" %}
{% endif %}
{% if locales[first_segment] %}
{% comment %} We're on a translated page — English link is always available {% endcomment %}
{% assign has_translations = true %}
{% comment %}
Extract the relative base path (independent of locale prefix and file extension).
E.g., "en/user/connections.md" and "be-rBY/user/connections.md" both resolve to "user/connections".
{% endcomment %}
{% assign remaining_parts = path_parts | slice: 1, path_parts.size %}
{% assign en_path = remaining_parts | join: "/" | replace: ".md", "" %}
{% else %}
{% comment %} Check if any translated version exists {% endcomment %}
{% assign en_relative = current_path | replace: ".md", "" %}
{% for locale in locales %}
{% assign locale_code = locale[0] %}
{% assign locale_file = locale_code | append: "/" | append: en_relative | append: ".md" %}
{% for p in site.pages %}
{% if p.path == locale_file %}
{% assign has_translations = true %}
{% break %}
{% endif %}
{% endfor %}
{% if has_translations %}{% break %}{% endif %}
{% endfor %}
{% endif %}
{% assign base_path = remaining_parts | join: "/" | replace: ".md", "" %}
{% if has_translations %}
<details class="language-switcher" aria-label="Language options">
<summary class="language-switcher-btn" title="View in another language">
🌐 <span class="lang-current">English</span>
</summary>
<ul class="language-switcher-list">
{% if locales[first_segment] %}
{% comment %}
Pre-render the dropdown list items.
{% endcomment %}
{% capture dropdown_items %}
{% if current_lang_code != "en" %}
{% if base_path == "index" %}
{% assign en_path = "en/" %}
{% else %}
{% assign en_path = "en/" | append: base_path %}
{% endif %}
<li><a href="{{ en_path | relative_url }}" lang="en">English</a></li>
{% endif %}
@@ -56,16 +56,12 @@
{% assign locale_code = locale[0] %}
{% assign locale_info = locale[1] %}
{% if locales[first_segment] %}
{% if locale_code == first_segment %}
{% continue %}
{% endif %}
{% assign locale_path = locale_code | append: "/" | append: en_path %}
{% else %}
{% assign locale_path = locale_code | append: "/" | append: en_relative %}
{% if locale_code == current_lang_code %}
{% continue %}
{% endif %}
{% assign locale_file = locale_path | append: ".md" %}
{% assign locale_file = locale_code | append: "/" | append: base_path | append: ".md" %}
{% assign page_exists = false %}
{% for p in site.pages %}
{% if p.path == locale_file %}
@@ -75,10 +71,29 @@
{% endfor %}
{% if page_exists %}
{% if base_path == "index" %}
{% assign locale_path = locale_code | append: "/" %}
{% else %}
{% assign locale_path = locale_code | append: "/" | append: base_path %}
{% endif %}
<li><a href="{{ locale_path | relative_url }}" lang="{{ locale_code }}" {% if locale_info.dir == "rtl" %}dir="rtl"{% endif %}>{{ locale_info.name }}</a></li>
{% endif %}
{% endfor %}
</ul>
</details>
{% endif %}
{% endcapture %}
{% assign dropdown_items_stripped = dropdown_items | strip %}
{% comment %}
Only render the switcher if there is at least one other language option available.
{% endcomment %}
{% if dropdown_items_stripped != "" %}
<details class="language-switcher" aria-label="Language options">
<summary class="language-switcher-btn" title="View in another language">
🌐 <span class="lang-current">{{ current_lang_name }}</span>
</summary>
<ul class="language-switcher-list">
{{ dropdown_items }}
</ul>
</details>
{% endif %}
{% endif %}

View File

@@ -1,5 +1,6 @@
---
title: Adding a Feature Module
parent: Developer Guide
nav_order: 3
last_updated: 2026-05-13
aliases:

View File

@@ -1,5 +1,6 @@
---
title: Architecture
parent: Developer Guide
nav_order: 1
last_updated: 2026-05-13
aliases:

View File

@@ -1,5 +1,6 @@
---
title: Codebase
parent: Developer Guide
nav_order: 2
last_updated: 2026-05-13
aliases:

View File

@@ -1,5 +1,6 @@
---
title: Contributing
parent: Developer Guide
nav_order: 8
last_updated: 2026-05-13
aliases:

View File

@@ -1,5 +1,6 @@
---
title: Measurement & Formatting
parent: Developer Guide
nav_order: 9
last_updated: 2026-05-13
aliases:

View File

@@ -1,5 +1,6 @@
---
title: Navigation & Deep Links
parent: Developer Guide
nav_order: 4
last_updated: 2026-05-13
aliases:

View File

@@ -1,5 +1,6 @@
---
title: Persistence
parent: Developer Guide
nav_order: 6
last_updated: 2026-05-13
aliases:

View File

@@ -1,5 +1,6 @@
---
title: Testing
parent: Developer Guide
nav_order: 7
last_updated: 2026-05-13
aliases:

View File

@@ -1,5 +1,6 @@
---
title: Transport
parent: Developer Guide
nav_order: 5
last_updated: 2026-05-13
aliases:

View File

@@ -1,46 +0,0 @@
---
title: Translations
layout: default
nav_order: 99
---
# Translations
This documentation is translated by the community via [Crowdin](https://crowdin.com/project/meshtastic-android). Translations appear here automatically as volunteers contribute them.
## Available Languages
{% assign any_locale_exists = false %}
{% for locale in site.data.locales %}
{% assign locale_code = locale[0] %}
{% assign locale_info = locale[1] %}
{% assign locale_prefix = locale_code | append: "/" %}
{% assign has_content = false %}
{% for p in site.pages %}
{% assign page_path_check = p.path | slice: 0, locale_prefix.size %}
{% if page_path_check == locale_prefix %}
{% assign has_content = true %}
{% assign any_locale_exists = true %}
{% break %}
{% endif %}
{% endfor %}
{% if has_content %}
- [{{ locale_info.name }}]({{ locale_code }}/) ({{ locale_code }})
{% endif %}
{% endfor %}
{% unless any_locale_exists %}
> No translations available yet. Want to help? [Join our Crowdin project →](https://crowdin.com/project/meshtastic-android)
{% endunless %}
---
## Contributing Translations
1. Visit [Crowdin](https://crowdin.com/project/meshtastic-android)
2. Select a language
3. Translate the User Guide documentation files
4. Translations are automatically synced to this site via PR
Translation coverage and quality are tracked per-language. Pages without full translation show the English original for untranslated sections.

View File

@@ -1,5 +1,6 @@
---
title: Connections
parent: User Guide
nav_order: 2
last_updated: 2026-05-13
description: Connect your phone or desktop to a Meshtastic radio via Bluetooth, USB, or TCP/IP.

View File

@@ -1,5 +1,6 @@
---
title: Desktop App
parent: User Guide
nav_order: 14
last_updated: 2026-05-13
description: Install and use the Meshtastic Desktop app on Linux, macOS, and Windows — connections, feature parity, and keyboard shortcuts.

View File

@@ -1,5 +1,6 @@
---
title: Discovery
parent: User Guide
nav_order: 12
last_updated: 2026-05-13
description: Explore your mesh network — traceroute paths, neighbor maps, and node discovery tools.

View File

@@ -1,5 +1,6 @@
---
title: Firmware Updates
parent: User Guide
nav_order: 13
last_updated: 2026-05-13
description: Update your radio firmware over Bluetooth — OTA process, version channels, pre-flight checks, and recovery.

View File

@@ -1,5 +1,6 @@
---
title: Map & Waypoints
parent: User Guide
nav_order: 6
last_updated: 2026-05-13
description: View node positions on the map, create and share waypoints, and manage position sharing and privacy.

View File

@@ -1,5 +1,6 @@
---
title: Messages & Channels
parent: User Guide
nav_order: 3
last_updated: 2026-05-13
description: Send and receive messages, manage channels, configure encryption, and use quick chat, reactions, and message actions.

View File

@@ -1,5 +1,6 @@
---
title: MQTT
parent: User Guide
nav_order: 11
last_updated: 2026-05-13
description: Bridge your mesh to the internet — MQTT broker setup, encryption layers, and map reporting.

View File

@@ -1,5 +1,6 @@
---
title: Node Metrics
parent: User Guide
nav_order: 5
last_updated: 2026-05-13
description: Telemetry dashboards for each mesh node — device health, environment sensors, signal quality, power, traceroute, and position history.

View File

@@ -1,5 +1,6 @@
---
title: Nodes
parent: User Guide
nav_order: 4
last_updated: 2026-05-13
description: Browse, filter, and sort mesh nodes — view details, signal quality, roles, and quick actions.

View File

@@ -1,5 +1,6 @@
---
title: Getting Started
parent: User Guide
nav_order: 1
last_updated: 2026-05-13
description: First-launch setup — permissions, onboarding flow, and next steps after connecting your radio.

View File

@@ -1,5 +1,6 @@
---
title: Settings — Modules & Admin
parent: User Guide
nav_order: 8
last_updated: 2026-05-13
description: Configure optional feature modules (MQTT, telemetry, canned messages, TAK, and more) and perform device administration.

View File

@@ -1,5 +1,6 @@
---
title: Settings — Radio & User
parent: User Guide
nav_order: 7
last_updated: 2026-05-13
description: Configure your radio hardware, LoRa presets, user profile, position sharing, power management, and security.

View File

@@ -1,5 +1,6 @@
---
title: How the Meshtastic Signal Meter Works
parent: User Guide
nav_order: 15
last_updated: 2026-05-13
description: How the signal meter calculates quality from RSSI and SNR — LoRa spread spectrum, presets, and what the bars really mean.

View File

@@ -1,5 +1,6 @@
---
title: TAK Integration
parent: User Guide
nav_order: 10
last_updated: 2026-05-13
description: Interoperate with ATAK and WinTAK — CoT position sharing, TAK roles, and plugin setup.

View File

@@ -1,5 +1,6 @@
---
title: Telemetry & Sensors
parent: User Guide
nav_order: 9
last_updated: 2026-05-13
description: Sensor data on the mesh — supported environment, air quality, and power sensors, plus configuration and viewing guides.