diff --git a/tools/build-docs.mjs b/tools/build-docs.mjs index 4ee51e9ab..cada7c0e1 100644 --- a/tools/build-docs.mjs +++ b/tools/build-docs.mjs @@ -14,6 +14,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { getSiteUrl } from '../website/src/lib/site-url.mjs'; +import { translatedLocales } from '../website/src/lib/locales.mjs'; // ============================================================================= // Configuration @@ -41,10 +42,6 @@ const LLM_EXCLUDE_PATTERNS = [ // Note: Files/dirs starting with _ (like _STYLE_GUIDE.md, _archive/) are excluded in shouldExcludeFromLlm() ]; -// Non-root locales — their docs duplicate English content and should not appear in llms-full.txt. -// Update this list when adding new i18n locales in website/astro.config.mjs. -const LLM_EXCLUDE_LOCALES = ['zh-cn', 'fr']; - // ============================================================================= // Main Entry Point /** @@ -293,7 +290,7 @@ function shouldExcludeFromLlm(filePath) { if (pathParts.some((part) => part.startsWith('_'))) return true; // Exclude non-root locale directories (translations duplicate English content) - if (LLM_EXCLUDE_LOCALES.some((locale) => filePath.startsWith(`${locale}/`) || filePath.startsWith(`${locale}${path.sep}`))) return true; + if (translatedLocales.some((locale) => filePath.startsWith(`${locale}/`) || filePath.startsWith(`${locale}${path.sep}`))) return true; // Check configured patterns return LLM_EXCLUDE_PATTERNS.some((pattern) => filePath.includes(pattern)); diff --git a/website/astro.config.mjs b/website/astro.config.mjs index b0f44d492..9d7efd99e 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -5,6 +5,7 @@ import sitemap from '@astrojs/sitemap'; import rehypeMarkdownLinks from './src/rehype-markdown-links.js'; import rehypeBasePaths from './src/rehype-base-paths.js'; import { getSiteUrl } from './src/lib/site-url.mjs'; +import { locales } from './src/lib/locales.mjs'; const siteUrl = getSiteUrl(); const urlParts = new URL(siteUrl); @@ -45,22 +46,9 @@ export default defineConfig({ title: 'BMAD Method', tagline: 'AI-driven agile development with specialized agents and workflows that scale from bug fixes to enterprise platforms.', - // i18n: English as root (no URL prefix), Chinese at /zh-cn/, French at /fr/ + // i18n: locale config from shared module (website/src/lib/locales.mjs) defaultLocale: 'root', - locales: { - root: { - label: 'English', - lang: 'en', - }, - 'zh-cn': { - label: '简体中文', - lang: 'zh-CN', - }, - fr: { - label: 'Français', - lang: 'fr-FR', - }, - }, + locales, logo: { light: './public/img/bmad-light.png', diff --git a/website/src/lib/locales.mjs b/website/src/lib/locales.mjs new file mode 100644 index 000000000..ef7e273e9 --- /dev/null +++ b/website/src/lib/locales.mjs @@ -0,0 +1,32 @@ +/** + * Shared i18n locale configuration. + * + * Single source of truth for locale definitions used by: + * - website/astro.config.mjs (Starlight i18n) + * - tools/build-docs.mjs (llms-full.txt locale exclusion) + * - website/src/pages/404.astro (client-side locale redirect) + * + * The root locale (English) uses Starlight's 'root' key convention + * (no URL prefix). All other locales get a URL prefix matching their key. + */ + +export const locales = { + root: { + label: 'English', + lang: 'en', + }, + 'zh-cn': { + label: '简体中文', + lang: 'zh-CN', + }, + fr: { + label: 'Français', + lang: 'fr-FR', + }, +}; + +/** + * Non-root locale keys (the URL prefixes for translated content). + * @type {string[]} + */ +export const translatedLocales = Object.keys(locales).filter((k) => k !== 'root'); diff --git a/website/src/pages/404.astro b/website/src/pages/404.astro index 945411ec9..6ae826ab7 100644 --- a/website/src/pages/404.astro +++ b/website/src/pages/404.astro @@ -1,6 +1,7 @@ --- import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro'; import { getEntry } from 'astro:content'; +import { translatedLocales } from '../lib/locales.mjs'; const entry = await getEntry('docs', '404'); const { Content } = await entry.render(); @@ -12,12 +13,11 @@ const { Content } = await entry.render(); -