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:
James RichandCopilot authored and GitHub committed 2026-05-19 14:06:44 -05:00
1 parent 92cfbaee9b
commit 11bc37c968
42 files changed
+155 -110

No files matched your search

+17 -17
View File
@@ -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("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
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>
+3 -2
View File
@@ -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
View 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
+41 -40
View File
@@ -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"
+2 -1
View File
@@ -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">
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+6 -13
View File
@@ -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"),
+1 -1
View File
@@ -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).
+1 -1
View File
@@ -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;
+2 -2
View File
@@ -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}`);
+4 -2
View File
@@ -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}'`);