LibreMesh is used by community mesh networks in many places where English isn't the first language, and right now the docs and guides are English-only. The site is VitePress, which has built-in i18n, so adding languages is mostly a content effort, not an engineering one.
Goal
Ship the site in Spanish (/es/) and Brazilian Portuguese (/pt-BR/) as the first two non-English locales, with a language switcher in the nav and a friendly first-visit prompt for visitors whose browser is in one of those languages. Then grow from there.
Why this approach
- VitePress
locales does the heavy lifting. It generates the language menu in the nav automatically, sets <html lang="...">, and routes /es/... and /pt-BR/... to the right directory. No build pipeline changes, no new dependencies.
- Translations live in the same repo, as plain markdown under
docs/es/ and docs/pt-BR/, mirroring the English structure. The existing editLink config already points contributors to the right file on GitHub.
- Auto-generated pages stay in English (the package and profile pages are built from git submodules by
prepare_data.js — hundreds of them, not realistic to translate by hand). If a translated page links to one of them, we link out to the English version with a small "in English" hint, rather than mixing languages on the same page. Cleaner, easier to maintain.
- No Crowdin / Weblate / CI translation infra for v1. Plain PRs, native speakers reviewing. If we ever want machine translation, a small LLM-based script can be bolted on later (see "Future" below) without changing the directory layout.
Concrete changes
1. docs/.vitepress/config.mts — add the two locales:
locales: {
root: { label: 'English', lang: 'en' },
es: { label: 'Español', lang: 'es', link: '/es/' },
'pt-BR': { label: 'Português (BR)', lang: 'pt-BR', link: '/pt-BR/' },
},
That alone gives us the language menu in the nav.
2. Mirror the English content tree under docs/es/ and docs/pt-BR/. Start with the highest-traffic pages: what-is-libremesh, getting-started, guide/connecting, guide/packages-selection, guide/upgrade, build/, reference/configuration, development/. Everything else links out to English.
3. Localized nav/sidebar labels. Move nav() and sidebarGuide() to a per-locale config (or use the link + themeConfig override pattern) so the nav items read "Guía", "Referencia", etc. in Spanish and "Guia", "Referência" in pt-BR. If a label isn't translated yet, fall back to English — better than a half-translated menu.
4. First-visit browser-language prompt. Add a small theme/Layout.vue (≤30 lines) that, on first visit with no stored preference, checks navigator.language against ['es', 'pt-BR'] and shows a dismissible banner: "This site is also available in Español / Português (BR) — switch?" User choice is stored in localStorage so we don't ask again. No jarring redirect, no content flash.
5. Add docs/TRANSLATING.md with a tiny glossary (keep "Batman-adv", "mesh node", "LiMe" untranslated), tone notes, and the link-out convention.
What's intentionally out of scope for v1
- Translating the auto-generated package/profile pages.
- Server-side language detection (we're on GitHub Pages; would need a CDN in front).
- A translation management platform.
Future
- An LLM-powered translation script (suggested, not committed): a small
scripts/translate.mjs that reads docs/**/*.md, sends untranslated pages to a translation API, writes the result to docs/<lang>/ as a draft PR for a native speaker to review. The directory layout we pick today is compatible with that — no rework needed.
Acceptance criteria for the first PR
LibreMesh is used by community mesh networks in many places where English isn't the first language, and right now the docs and guides are English-only. The site is VitePress, which has built-in i18n, so adding languages is mostly a content effort, not an engineering one.
Goal
Ship the site in Spanish (
/es/) and Brazilian Portuguese (/pt-BR/) as the first two non-English locales, with a language switcher in the nav and a friendly first-visit prompt for visitors whose browser is in one of those languages. Then grow from there.Why this approach
localesdoes the heavy lifting. It generates the language menu in the nav automatically, sets<html lang="...">, and routes/es/...and/pt-BR/...to the right directory. No build pipeline changes, no new dependencies.docs/es/anddocs/pt-BR/, mirroring the English structure. The existingeditLinkconfig already points contributors to the right file on GitHub.prepare_data.js— hundreds of them, not realistic to translate by hand). If a translated page links to one of them, we link out to the English version with a small "in English" hint, rather than mixing languages on the same page. Cleaner, easier to maintain.Concrete changes
1.
docs/.vitepress/config.mts— add the two locales:That alone gives us the language menu in the nav.
2. Mirror the English content tree under
docs/es/anddocs/pt-BR/. Start with the highest-traffic pages:what-is-libremesh,getting-started,guide/connecting,guide/packages-selection,guide/upgrade,build/,reference/configuration,development/. Everything else links out to English.3. Localized nav/sidebar labels. Move
nav()andsidebarGuide()to a per-locale config (or use thelink+themeConfigoverride pattern) so the nav items read "Guía", "Referencia", etc. in Spanish and "Guia", "Referência" in pt-BR. If a label isn't translated yet, fall back to English — better than a half-translated menu.4. First-visit browser-language prompt. Add a small
theme/Layout.vue(≤30 lines) that, on first visit with no stored preference, checksnavigator.languageagainst['es', 'pt-BR']and shows a dismissible banner: "This site is also available in Español / Português (BR) — switch?" User choice is stored inlocalStorageso we don't ask again. No jarring redirect, no content flash.5. Add
docs/TRANSLATING.mdwith a tiny glossary (keep "Batman-adv", "mesh node", "LiMe" untranslated), tone notes, and the link-out convention.What's intentionally out of scope for v1
Future
scripts/translate.mjsthat readsdocs/**/*.md, sends untranslated pages to a translation API, writes the result todocs/<lang>/as a draft PR for a native speaker to review. The directory layout we pick today is compatible with that — no rework needed.Acceptance criteria for the first PR
esandpt-BRadded tolocalesinconfig.mts./es/and/pt-BR/.what-is-libremesh,getting-started, andguide/connectingtranslated in both languages.es-*/pt-BRbrowser locales and remembers the choice.pnpm buildstill passes.