mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-05-24 06:40:37 -04:00
docs: move English sources into docs/en/ locale folder (#5501)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
34
.github/workflows/docs-governance.yml
vendored
34
.github/workflows/docs-governance.yml
vendored
@@ -39,7 +39,7 @@ jobs:
|
||||
'^feature/.*/src/commonMain/.*/(ui|component|screen)/|^feature/.*/src/androidMain/.*/ui/|^core/ui/src/commonMain/' \
|
||||
| grep -v 'Test\|Preview\|__Snapshots__' || true)
|
||||
|
||||
docs_changed=$(echo "$changed" | grep -E '^docs/(user|developer)/' || true)
|
||||
docs_changed=$(echo "$changed" | grep -E '^docs/en/(user|developer)/' || true)
|
||||
|
||||
echo "views_changed<<EOF" >> "$GITHUB_OUTPUT"
|
||||
echo "$views_changed" >> "$GITHUB_OUTPUT"
|
||||
@@ -64,9 +64,9 @@ jobs:
|
||||
const body = [
|
||||
'## 📄 Docs staleness check — advisory',
|
||||
'',
|
||||
'This PR modifies user-facing UI source files but does not update any page under `docs/user/` or `docs/developer/`.',
|
||||
'This PR modifies user-facing UI source files but does not update any page under `docs/en/user/` or `docs/en/developer/`.',
|
||||
'',
|
||||
'> ⚠️ Doc changes propagate to **3 consumers**: in-app docs browser, Jekyll site (GitHub Pages), and meshtastic.org (Docusaurus sync). Updating a page in `docs/` automatically flows to all three.',
|
||||
'> ⚠️ Doc changes propagate to **3 consumers**: in-app docs browser, Jekyll site (GitHub Pages), and meshtastic.org (Docusaurus sync). Updating a page in `docs/en/` automatically flows to all three.',
|
||||
'',
|
||||
'**Changed source files:**',
|
||||
'```',
|
||||
@@ -76,19 +76,19 @@ jobs:
|
||||
'**What to check:**',
|
||||
'| Changed area | Likely doc page |',
|
||||
'|---|---|',
|
||||
'| `feature/messaging/` | `docs/user/messages-and-channels.md` |',
|
||||
'| `feature/node/` | `docs/user/nodes.md` or `docs/user/node-metrics.md` |',
|
||||
'| `feature/map/` | `docs/user/map-and-waypoints.md` |',
|
||||
'| `feature/connections/` | `docs/user/connections.md` |',
|
||||
'| `feature/settings/` | `docs/user/settings-radio-user.md` or `docs/user/settings-module-admin.md` |',
|
||||
'| `feature/firmware/` | `docs/user/firmware.md` |',
|
||||
'| `feature/intro/` | `docs/user/onboarding.md` |',
|
||||
'| `feature/discovery/` | `docs/user/discovery.md` |',
|
||||
'| `feature/messaging/` | `docs/en/user/messages-and-channels.md` |',
|
||||
'| `feature/node/` | `docs/en/user/nodes.md` or `docs/en/user/node-metrics.md` |',
|
||||
'| `feature/map/` | `docs/en/user/map-and-waypoints.md` |',
|
||||
'| `feature/connections/` | `docs/en/user/connections.md` |',
|
||||
'| `feature/settings/` | `docs/en/user/settings-radio-user.md` or `docs/en/user/settings-module-admin.md` |',
|
||||
'| `feature/firmware/` | `docs/en/user/firmware.md` |',
|
||||
'| `feature/intro/` | `docs/en/user/onboarding.md` |',
|
||||
'| `feature/discovery/` | `docs/en/user/discovery.md` |',
|
||||
'| `feature/docs/` | Internal docs infrastructure |',
|
||||
'| `core/ui/` | `docs/developer/codebase.md` or component-specific user pages |',
|
||||
'| `core/ui/` | `docs/en/developer/codebase.md` or component-specific user pages |',
|
||||
'',
|
||||
'**New page checklist** (if adding a new doc page):',
|
||||
'1. Create the `.md` file in `docs/user/` or `docs/developer/` with `last_updated` frontmatter',
|
||||
'1. Create the `.md` file in `docs/en/user/` or `docs/en/developer/` with `last_updated` frontmatter',
|
||||
'2. Register in `DocBundleLoader.kt` with string resources (in-app browser)',
|
||||
'3. Jekyll and Docusaurus sync pick up new pages automatically — no config change needed',
|
||||
'',
|
||||
@@ -141,14 +141,14 @@ jobs:
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: existing.id,
|
||||
body: '## ✅ Docs staleness check passed\n\nThis PR includes updates to `docs/` alongside the source changes. Thank you!',
|
||||
body: '## ✅ Docs staleness check passed\n\nThis PR includes updates to `docs/en/` alongside the source changes. Thank you!',
|
||||
});
|
||||
}
|
||||
|
||||
- name: Advisory status
|
||||
if: steps.changed.outputs.stale == 'true'
|
||||
run: |
|
||||
echo "::warning::UI source files changed without corresponding docs/ updates."
|
||||
echo "::warning::UI source files changed without corresponding docs/en/ updates."
|
||||
echo "Add the 'skip-docs-check' label if this PR does not require a doc update."
|
||||
echo "NOTE: This check is advisory while docs coverage matures across platforms."
|
||||
echo "To upgrade to blocking, change this step to 'exit 1'."
|
||||
@@ -170,7 +170,7 @@ jobs:
|
||||
node-version: "24"
|
||||
|
||||
- name: Validate internal links
|
||||
run: node scripts/validate-doc-links.js docs
|
||||
run: node scripts/validate-doc-links.js docs/en
|
||||
|
||||
- name: Check doc coverage
|
||||
run: node scripts/check-doc-coverage.js .
|
||||
@@ -179,7 +179,7 @@ jobs:
|
||||
run: |
|
||||
loader="feature/docs/src/commonMain/kotlin/org/meshtastic/feature/docs/data/DocBundleLoader.kt"
|
||||
missing=0
|
||||
for f in docs/user/*.md docs/developer/*.md; do
|
||||
for f in docs/en/user/*.md docs/en/developer/*.md; do
|
||||
slug=$(basename "$f" .md)
|
||||
if ! grep -q "\"$slug\"" "$loader"; then
|
||||
echo "ERROR: $slug not registered in DocBundleLoader.kt"
|
||||
|
||||
@@ -100,9 +100,10 @@ abstract class GenerateDocsBundleTask : DefaultTask() {
|
||||
val indexEntries = mutableListOf<String>()
|
||||
var pageCount = 0
|
||||
|
||||
// Process English user and developer directories
|
||||
// Process English user and developer directories (under docs/en/)
|
||||
val enDir = File(src, "en")
|
||||
listOf("user", "developer").forEach { section ->
|
||||
val sectionDir = File(src, section)
|
||||
val sectionDir = File(enDir, section)
|
||||
if (!sectionDir.exists()) return@forEach
|
||||
|
||||
sectionDir.listFiles { f -> f.extension == "md" }?.sortedBy { it.name }?.forEach { mdFile ->
|
||||
@@ -143,8 +144,9 @@ abstract class GenerateDocsBundleTask : DefaultTask() {
|
||||
|
||||
// Process Crowdin locale directories: docs/{qualifier}/user/*.md
|
||||
// Crowdin %android_code% produces: fr, pt-rBR, zh-rCN, zh-rTW
|
||||
// Skip "en" since English sources are handled above.
|
||||
val localePattern = Regex("^[a-z]{2,3}(-r[A-Z]{2})?$")
|
||||
src.listFiles { f -> f.isDirectory && localePattern.matches(f.name) }
|
||||
src.listFiles { f -> f.isDirectory && localePattern.matches(f.name) && f.name != "en" }
|
||||
?.sortedBy { it.name }
|
||||
?.forEach { localeDir ->
|
||||
val locale = localeDir.name
|
||||
@@ -198,7 +200,7 @@ abstract class GenerateDocsBundleTask : DefaultTask() {
|
||||
File(cssDir, "docs.css").writeText(generateCss())
|
||||
|
||||
// Write locales manifest (for consumers that need to know available translations)
|
||||
val localesManifest = src.listFiles { f -> f.isDirectory && localePattern.matches(f.name) }
|
||||
val localesManifest = src.listFiles { f -> f.isDirectory && localePattern.matches(f.name) && f.name != "en" }
|
||||
?.map { it.name }?.sorted() ?: emptyList()
|
||||
val manifestFile = File(out, "locales.json")
|
||||
manifestFile.writeText(localesManifest.joinToString(", ", "[", "]") { "\"$it\"" })
|
||||
@@ -258,7 +260,7 @@ abstract class GenerateDocsBundleTask : DefaultTask() {
|
||||
.replace(Regex("^---[\\s\\S]*?---\\s*", RegexOption.MULTILINE), "")
|
||||
.replace("&", "&").replace("<", "<").replace(">", ">")
|
||||
val dir = if (locale == "ar") "rtl" else "ltr"
|
||||
// Locale pages are one level deeper: docs/{locale}/user/foo.html vs docs/user/foo.html
|
||||
// Locale pages are one level deeper: docs/{locale}/user/foo.html vs docs/en/user/foo.html
|
||||
val cssPath = if (locale != "en") "../../styles/docs.css" else "../styles/docs.css"
|
||||
return """
|
||||
|<!DOCTYPE html>
|
||||
|
||||
@@ -11,10 +11,11 @@ files:
|
||||
- source: /fastlane/metadata/android/en-US/changelogs/default.txt
|
||||
translation: /fastlane/metadata/android/%locale%/changelogs/%original_file_name%
|
||||
# In-app docs — user guide only (developer guide is English-only)
|
||||
# English sources live under docs/en/; translations land at docs/{android_code}/
|
||||
# Uses %android_code% to output Android/CMP qualifier format directly (pt-rBR, zh-rCN, fr)
|
||||
- source: /docs/user/*.md
|
||||
- source: /docs/en/user/*.md
|
||||
translation: /docs/%android_code%/user/%original_file_name%
|
||||
type: md
|
||||
- source: /docs/index.md
|
||||
- source: /docs/en/index.md
|
||||
translation: /docs/%android_code%/%original_file_name%
|
||||
type: md
|
||||
|
||||
45
docs/README.md
Normal file
45
docs/README.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Documentation Structure
|
||||
|
||||
This directory contains the source documentation for the Meshtastic Android/Desktop/iOS app.
|
||||
It serves three consumers:
|
||||
|
||||
1. **In-app docs browser** — bundled via Compose Resources at build time
|
||||
2. **Jekyll site** — GitHub Pages (this directory is the Jekyll source root)
|
||||
3. **meshtastic.org** — Docusaurus sync (upstream consumption)
|
||||
|
||||
## Locale Layout
|
||||
|
||||
```
|
||||
docs/
|
||||
├── _config.yml, _data/, _layouts/, _sass/ ← Jekyll site infrastructure
|
||||
├── en/ ← English source (edit here)
|
||||
│ ├── user/ ← User Guide pages
|
||||
│ ├── 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
|
||||
├── fr-rFR/ ← French (Crowdin-generated)
|
||||
│ └── user/ ← Translated user guide
|
||||
├── de-rDE/ ← German (Crowdin-generated)
|
||||
│ └── user/
|
||||
└── ... ← Other locales
|
||||
```
|
||||
|
||||
## Editing Guidelines
|
||||
|
||||
- **English source**: Edit files under `docs/en/`. These are the authoritative source.
|
||||
- **Translations**: Do **not** edit files in locale folders directly. They are auto-generated
|
||||
by [Crowdin](https://crowdin.com/project/meshtastic-android) and will be overwritten on sync.
|
||||
Contribute translations via Crowdin instead.
|
||||
- **Adding a page**: Create the `.md` file in `docs/en/user/` or `docs/en/developer/`, then
|
||||
register it in `feature/docs/.../DocBundleLoader.kt` for in-app bundling.
|
||||
|
||||
## How Translations Work
|
||||
|
||||
1. English source files (`docs/en/user/*.md`) are uploaded to Crowdin as translation sources
|
||||
2. Volunteers translate via the Crowdin web UI
|
||||
3. Crowdin PRs land translated files at `docs/{android_code}/user/*.md` (e.g., `fr-rFR`, `pt-rBR`)
|
||||
4. At build time, the Gradle `syncTranslatedDocsToComposeResources` task bundles them into
|
||||
locale-qualified Compose Resources for the in-app reader
|
||||
5. The in-app `DocBundleLoader` tries the user's locale first, then falls back to English
|
||||
@@ -28,12 +28,12 @@ color_scheme: meshtastic
|
||||
# Default front-matter for pages in subdirectories
|
||||
defaults:
|
||||
- scope:
|
||||
path: "user/"
|
||||
path: "en/user/"
|
||||
values:
|
||||
parent: User Guide
|
||||
layout: default
|
||||
- scope:
|
||||
path: "developer/"
|
||||
path: "en/developer/"
|
||||
values:
|
||||
parent: Developer Guide
|
||||
layout: default
|
||||
@@ -41,229 +41,229 @@ defaults:
|
||||
# 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"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: ar
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "be/"
|
||||
path: "be"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: be
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "bg/"
|
||||
path: "bg"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: bg
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "ca/"
|
||||
path: "ca"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: ca
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "cs/"
|
||||
path: "cs"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: cs
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "de/"
|
||||
path: "de"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: de
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "el/"
|
||||
path: "el"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: el
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "es/"
|
||||
path: "es"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: es
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "et/"
|
||||
path: "et"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: et
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "fi/"
|
||||
path: "fi"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: fi
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "fr/"
|
||||
path: "fr"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: fr
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "ga/"
|
||||
path: "ga"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: ga
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "gl/"
|
||||
path: "gl"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: gl
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "he/"
|
||||
path: "he"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: he
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "hr/"
|
||||
path: "hr"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: hr
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "ht/"
|
||||
path: "ht"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: ht
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "hu/"
|
||||
path: "hu"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: hu
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "is/"
|
||||
path: "is"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: is
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "it/"
|
||||
path: "it"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: it
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "ja/"
|
||||
path: "ja"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: ja
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "ko/"
|
||||
path: "ko"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: ko
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "lt/"
|
||||
path: "lt"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: lt
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "nl/"
|
||||
path: "nl"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: nl
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "no/"
|
||||
path: "no"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: no
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "pl/"
|
||||
path: "pl"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: pl
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "pt/"
|
||||
path: "pt"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: pt
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "pt-rBR/"
|
||||
path: "pt-rBR"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: pt-rBR
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "ro/"
|
||||
path: "ro"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: ro
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "ru/"
|
||||
path: "ru"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: ru
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "sk/"
|
||||
path: "sk"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: sk
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "sl/"
|
||||
path: "sl"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: sl
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "sq/"
|
||||
path: "sq"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: sq
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "sr/"
|
||||
path: "sr"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: sr
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "sv/"
|
||||
path: "sv"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: sv
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "tr/"
|
||||
path: "tr"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: tr
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "uk/"
|
||||
path: "uk"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: uk
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "zh-rCN/"
|
||||
path: "zh-rCN"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: zh-rCN
|
||||
nav_exclude: true
|
||||
- scope:
|
||||
path: "zh-rTW/"
|
||||
path: "zh-rTW"
|
||||
values:
|
||||
layout: locale_page
|
||||
locale: zh-rTW
|
||||
@@ -285,5 +285,6 @@ exclude:
|
||||
- Gemfile
|
||||
- Gemfile.lock
|
||||
- assets/screenshots/.gitkeep
|
||||
- README.md
|
||||
- "*.sh"
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@ layout: default
|
||||
{% assign page_path = page.path %}
|
||||
{% assign path_parts = page_path | split: "/" %}
|
||||
{% assign remaining_parts = path_parts | slice: 1, path_parts.size %}
|
||||
{% assign en_path = remaining_parts | join: "/" | replace: ".md", "" %}
|
||||
{% assign en_relative = remaining_parts | join: "/" | replace: ".md", "" %}
|
||||
{% assign en_path = "en/" | append: en_relative %}
|
||||
|
||||
<div class="locale-page-banner" {% if locale_info.dir == "rtl" %}dir="rtl"{% endif %}>
|
||||
<p class="locale-notice">
|
||||
|
||||
@@ -59,24 +59,16 @@ kotlin {
|
||||
*/
|
||||
val syncDocsToComposeResources by
|
||||
tasks.registering(Sync::class) {
|
||||
description = "Syncs docs/ markdown source into composeResources for in-app bundling"
|
||||
description = "Syncs docs/en/ markdown source into composeResources for in-app bundling"
|
||||
group = "docs"
|
||||
|
||||
val docsSourceDir = rootProject.layout.projectDirectory.dir("docs")
|
||||
val docsEnDir = rootProject.layout.projectDirectory.dir("docs/en")
|
||||
val screenshotsDir = rootProject.layout.projectDirectory.dir("docs/screenshots")
|
||||
val composeResourcesTarget = layout.projectDirectory.dir("src/commonMain/composeResources/files/docs")
|
||||
|
||||
from(docsSourceDir) {
|
||||
from(docsEnDir) {
|
||||
include("user/**/*.md")
|
||||
include("developer/**/*.md")
|
||||
// Exclude Jekyll/site-only files that are not needed in-app
|
||||
exclude("_config.yml")
|
||||
exclude("_data/**")
|
||||
exclude("_includes/**")
|
||||
exclude("_layouts/**")
|
||||
exclude("index.md")
|
||||
exclude("assets/**")
|
||||
exclude("Gemfile*")
|
||||
}
|
||||
|
||||
// FR-038: Bundle screenshots into assets/screenshots/ to match markdown image paths.
|
||||
@@ -117,8 +109,7 @@ val syncTranslatedDocsToComposeResources by
|
||||
from(docsDir) {
|
||||
// Crowdin outputs dirs in Android qualifier format (fr, pt-rBR, zh-rCN)
|
||||
include("*/user/**/*.md")
|
||||
exclude("user/**")
|
||||
exclude("developer/**")
|
||||
exclude("en/**")
|
||||
exclude("_*/**")
|
||||
exclude("assets/**")
|
||||
exclude("screenshots/**")
|
||||
@@ -133,6 +124,8 @@ val syncTranslatedDocsToComposeResources by
|
||||
if (segments.size >= 3) {
|
||||
val qualifier = segments[0]
|
||||
val rest = segments.drop(1).joinToString("/")
|
||||
// Output: files/{locale}/docs/user/page.md
|
||||
// English source lives at: docs/en/$rest
|
||||
path = "$qualifier/docs/$rest"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,7 +241,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"onboarding",
|
||||
CoreRes.string.doc_title_onboarding,
|
||||
CoreRes.string.doc_keywords_onboarding,
|
||||
"docs/user/onboarding.html",
|
||||
"en/user/onboarding.html",
|
||||
1,
|
||||
listOf("first-launch", "setup", "intro"),
|
||||
3200,
|
||||
@@ -251,7 +251,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"connections",
|
||||
CoreRes.string.doc_title_connections,
|
||||
CoreRes.string.doc_keywords_connections,
|
||||
"docs/user/connections.html",
|
||||
"en/user/connections.html",
|
||||
2,
|
||||
listOf("bluetooth", "usb", "tcp", "pairing"),
|
||||
4100,
|
||||
@@ -261,7 +261,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"messages-and-channels",
|
||||
CoreRes.string.doc_title_messages,
|
||||
CoreRes.string.doc_keywords_messages,
|
||||
"docs/user/messages-and-channels.html",
|
||||
"en/user/messages-and-channels.html",
|
||||
3,
|
||||
listOf("channels", "direct-messages", "messaging", "conversations"),
|
||||
4500,
|
||||
@@ -271,7 +271,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"nodes",
|
||||
CoreRes.string.doc_title_nodes,
|
||||
CoreRes.string.doc_keywords_nodes,
|
||||
"docs/user/nodes.html",
|
||||
"en/user/nodes.html",
|
||||
4,
|
||||
listOf("node-list", "mesh-nodes", "peers"),
|
||||
3800,
|
||||
@@ -281,7 +281,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"node-metrics",
|
||||
CoreRes.string.doc_title_node_metrics,
|
||||
CoreRes.string.doc_keywords_node_metrics,
|
||||
"docs/user/node-metrics.html",
|
||||
"en/user/node-metrics.html",
|
||||
5,
|
||||
listOf("metrics", "telemetry", "device-metrics", "signal"),
|
||||
5200,
|
||||
@@ -291,7 +291,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"map-and-waypoints",
|
||||
CoreRes.string.doc_title_map,
|
||||
CoreRes.string.doc_keywords_map,
|
||||
"docs/user/map-and-waypoints.html",
|
||||
"en/user/map-and-waypoints.html",
|
||||
6,
|
||||
listOf("map", "waypoints", "gps", "location"),
|
||||
3600,
|
||||
@@ -301,7 +301,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"settings-radio-user",
|
||||
CoreRes.string.doc_title_settings_radio,
|
||||
CoreRes.string.doc_keywords_settings_radio,
|
||||
"docs/user/settings-radio-user.html",
|
||||
"en/user/settings-radio-user.html",
|
||||
7,
|
||||
listOf("settings", "radio-config", "user-config", "lora"),
|
||||
6800,
|
||||
@@ -311,7 +311,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"settings-module-admin",
|
||||
CoreRes.string.doc_title_settings_module,
|
||||
CoreRes.string.doc_keywords_settings_module,
|
||||
"docs/user/settings-module-admin.html",
|
||||
"en/user/settings-module-admin.html",
|
||||
8,
|
||||
listOf("modules", "module-config", "administration"),
|
||||
5500,
|
||||
@@ -321,7 +321,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"telemetry-and-sensors",
|
||||
CoreRes.string.doc_title_telemetry,
|
||||
CoreRes.string.doc_keywords_telemetry,
|
||||
"docs/user/telemetry-and-sensors.html",
|
||||
"en/user/telemetry-and-sensors.html",
|
||||
9,
|
||||
listOf("sensors", "environment", "weather", "power-metrics"),
|
||||
4800,
|
||||
@@ -331,7 +331,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"tak",
|
||||
CoreRes.string.doc_title_tak,
|
||||
CoreRes.string.doc_keywords_tak,
|
||||
"docs/user/tak.html",
|
||||
"en/user/tak.html",
|
||||
10,
|
||||
listOf("tak", "atak", "team-awareness-kit"),
|
||||
2400,
|
||||
@@ -341,7 +341,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"mqtt",
|
||||
CoreRes.string.doc_title_mqtt,
|
||||
CoreRes.string.doc_keywords_mqtt,
|
||||
"docs/user/mqtt.html",
|
||||
"en/user/mqtt.html",
|
||||
11,
|
||||
listOf("mqtt", "internet-bridge", "broker"),
|
||||
4200,
|
||||
@@ -351,7 +351,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"discovery",
|
||||
CoreRes.string.doc_title_discovery,
|
||||
CoreRes.string.doc_keywords_discovery,
|
||||
"docs/user/discovery.html",
|
||||
"en/user/discovery.html",
|
||||
12,
|
||||
listOf("mesh-discovery", "local-discovery", "network-scan"),
|
||||
2800,
|
||||
@@ -361,7 +361,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"firmware",
|
||||
CoreRes.string.doc_title_firmware,
|
||||
CoreRes.string.doc_keywords_firmware,
|
||||
"docs/user/firmware.html",
|
||||
"en/user/firmware.html",
|
||||
13,
|
||||
listOf("firmware", "update", "ota", "flash"),
|
||||
3400,
|
||||
@@ -371,7 +371,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"desktop",
|
||||
CoreRes.string.doc_title_desktop,
|
||||
CoreRes.string.doc_keywords_desktop,
|
||||
"docs/user/desktop.html",
|
||||
"en/user/desktop.html",
|
||||
14,
|
||||
listOf("desktop", "linux", "macos", "windows", "jvm"),
|
||||
3900,
|
||||
@@ -381,7 +381,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"signal-meter",
|
||||
CoreRes.string.doc_title_signal_meter,
|
||||
CoreRes.string.doc_keywords_signal_meter,
|
||||
"docs/user/signal-meter.html",
|
||||
"en/user/signal-meter.html",
|
||||
15,
|
||||
listOf("signal-quality", "signal-strength", "rssi", "snr"),
|
||||
3500,
|
||||
@@ -391,7 +391,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"units-and-locale",
|
||||
CoreRes.string.doc_title_units,
|
||||
CoreRes.string.doc_keywords_units,
|
||||
"docs/user/units-and-locale.html",
|
||||
"en/user/units-and-locale.html",
|
||||
16,
|
||||
listOf("measurement", "units", "locale", "metric", "imperial"),
|
||||
3800,
|
||||
@@ -401,7 +401,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"translate",
|
||||
CoreRes.string.doc_title_translate,
|
||||
CoreRes.string.doc_keywords_translate,
|
||||
"docs/user/translate.html",
|
||||
"en/user/translate.html",
|
||||
17,
|
||||
listOf("crowdin", "localization", "language", "i18n", "contribute"),
|
||||
3700,
|
||||
@@ -433,7 +433,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"architecture",
|
||||
"Architecture",
|
||||
"developer",
|
||||
"docs/developer/architecture.html",
|
||||
"en/developer/architecture.html",
|
||||
1,
|
||||
listOf("architecture", "kmp", "module", "layer", "core", "feature", "compose"),
|
||||
listOf("layers", "module-architecture", "kmp"),
|
||||
@@ -444,7 +444,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"codebase",
|
||||
"Codebase",
|
||||
"developer",
|
||||
"docs/developer/codebase.html",
|
||||
"en/developer/codebase.html",
|
||||
2,
|
||||
listOf("codebase", "repository", "layout", "gradle", "build", "namespace", "convention"),
|
||||
listOf("repository-layout", "project-structure", "source-code"),
|
||||
@@ -455,7 +455,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"adding-a-feature-module",
|
||||
"Adding a Feature Module",
|
||||
"developer",
|
||||
"docs/developer/adding-a-feature-module.html",
|
||||
"en/developer/adding-a-feature-module.html",
|
||||
3,
|
||||
listOf("module", "feature", "new", "create", "plugin", "di", "koin"),
|
||||
listOf("new-module", "feature-module", "module-guide"),
|
||||
@@ -466,7 +466,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"navigation-and-deep-links",
|
||||
"Navigation & Deep Links",
|
||||
"developer",
|
||||
"docs/developer/navigation-and-deep-links.html",
|
||||
"en/developer/navigation-and-deep-links.html",
|
||||
4,
|
||||
listOf("navigation", "deeplink", "route", "navkey", "backstack", "typed"),
|
||||
listOf("deeplinks", "navigation-3", "routes"),
|
||||
@@ -477,7 +477,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"transport",
|
||||
"Transport",
|
||||
"developer",
|
||||
"docs/developer/transport.html",
|
||||
"en/developer/transport.html",
|
||||
5,
|
||||
listOf("transport", "ble", "serial", "tcp", "radio", "connection"),
|
||||
listOf("ble", "serial", "tcp", "radio-transport"),
|
||||
@@ -488,7 +488,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"persistence",
|
||||
"Persistence",
|
||||
"developer",
|
||||
"docs/developer/persistence.html",
|
||||
"en/developer/persistence.html",
|
||||
6,
|
||||
listOf("room", "database", "datastore", "prefs", "storage", "migration"),
|
||||
listOf("room", "database", "datastore", "prefs"),
|
||||
@@ -499,7 +499,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"testing",
|
||||
"Testing",
|
||||
"developer",
|
||||
"docs/developer/testing.html",
|
||||
"en/developer/testing.html",
|
||||
7,
|
||||
listOf("test", "unit", "screenshot", "compose", "roborazzi", "ci"),
|
||||
listOf("tests", "unit-tests", "screenshot-tests"),
|
||||
@@ -510,7 +510,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"contributing",
|
||||
"Contributing",
|
||||
"developer",
|
||||
"docs/developer/contributing.html",
|
||||
"en/developer/contributing.html",
|
||||
8,
|
||||
listOf("contributing", "pull-request", "branch", "commit", "style", "pr"),
|
||||
listOf("contributing", "pull-request", "branch-naming"),
|
||||
@@ -521,7 +521,7 @@ class DefaultDocBundleLoader : DocBundleLoader {
|
||||
"measurement",
|
||||
"Measurement & Formatting",
|
||||
"developer",
|
||||
"docs/developer/measurement.html",
|
||||
"en/developer/measurement.html",
|
||||
9,
|
||||
listOf("formatter", "metric", "number", "locale", "temperature", "conversion", "api"),
|
||||
listOf("metric-formatter", "number-formatter", "measurement"),
|
||||
|
||||
@@ -12,7 +12,7 @@ const path = require("path");
|
||||
const { forEachDocPage } = require("./lib/frontmatter");
|
||||
|
||||
const REPO_ROOT = path.resolve(process.argv[2] || ".");
|
||||
const DOCS_DIR = path.join(REPO_ROOT, "docs");
|
||||
const DOCS_DIR = path.join(REPO_ROOT, "docs", "en");
|
||||
|
||||
// Map of feature module directory names to expected doc page slugs.
|
||||
// Modules not listed here are considered internal (no user-facing docs required).
|
||||
|
||||
@@ -13,7 +13,7 @@ const { parseFrontmatter, forEachDocPage } = require("./lib/frontmatter");
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const positional = args.filter(a => !a.startsWith("--"));
|
||||
const DOCS_DIR = path.resolve(positional[0] || "docs");
|
||||
const DOCS_DIR = path.resolve(positional[0] || path.join("docs", "en"));
|
||||
|
||||
const maxAgeArg = args.find(a => a.startsWith("--max-age-days="));
|
||||
const MAX_AGE_DAYS = maxAgeArg ? parseInt(maxAgeArg.split("=")[1], 10) : 180;
|
||||
|
||||
@@ -37,8 +37,8 @@ const IMAGE_EXTENSIONS = new Set([".png", ".jpg", ".jpeg", ".gif", ".svg", ".web
|
||||
const ANDROID_REPO_ROOT = positionalArgs.length > 0
|
||||
? path.resolve(positionalArgs[0])
|
||||
: path.resolve(__dirname, "..");
|
||||
const SRC_DOCS_DIR = path.join(ANDROID_REPO_ROOT, "docs");
|
||||
const SRC_SCREENSHOTS_DIR = path.join(SRC_DOCS_DIR, "assets", "screenshots");
|
||||
const SRC_DOCS_DIR = path.join(ANDROID_REPO_ROOT, "docs", "en");
|
||||
const SRC_SCREENSHOTS_DIR = path.join(ANDROID_REPO_ROOT, "docs", "assets", "screenshots");
|
||||
|
||||
if (!fs.existsSync(SRC_DOCS_DIR)) {
|
||||
console.error(`Error: docs directory not found at ${SRC_DOCS_DIR}`);
|
||||
|
||||
@@ -11,7 +11,9 @@ const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { discoverSlugs, forEachDocPage } = require("./lib/frontmatter");
|
||||
|
||||
const DOCS_DIR = path.resolve(process.argv[2] || "docs");
|
||||
const DOCS_DIR = path.resolve(process.argv[2] || path.join("docs", "en"));
|
||||
// Assets (screenshots) live at docs/ root, not inside the en/ locale folder
|
||||
const DOCS_ROOT = path.resolve(DOCS_DIR, "..");
|
||||
const IMAGE_EXTS = new Set([".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp"]);
|
||||
|
||||
// Collect known page slugs from both sections
|
||||
@@ -64,7 +66,7 @@ forEachDocPage(DOCS_DIR, (filePath, slug, section) => {
|
||||
if (/^https?:/.test(imgPath)) continue;
|
||||
|
||||
const resolved = imgPath.startsWith("/")
|
||||
? path.join(DOCS_DIR, imgPath)
|
||||
? path.join(DOCS_ROOT, imgPath)
|
||||
: path.resolve(path.dirname(filePath), imgPath);
|
||||
if (!fs.existsSync(resolved)) {
|
||||
console.log(` ERROR: ${section}/${slug}.md:${lineNum} — missing image '${imgPath}'`);
|
||||
|
||||
Reference in New Issue
Block a user