BxSites 的博客、版本化、i18n 与重定向三者都遵循“约定优于配置”——不需要在 bxsites.yaml 中打开任何开关,只要存在对应文件夹即可启用。
一、博客 把文章放到 docs/blog/posts/ 下,BxSites 就会构建分页的 /blog/、每个分类的分类页、每个自然年的年度归档页、每位作者页、RSS 订阅源以及 /blog/stats/,零配置。项目若没有 docs/blog/posts/ 文件夹,就没有博客。
用脚手架创建文章: bxSites post:new --title="My New Post" [--slug=] [--date=] [--authors=] [--categories=] [--tags=] [--draft] --draft 默认为 true;传 --!draft 可立即发布。
docs/blog/posts/ 下任意深度的每个 .md 都是一篇文章。子文件夹纯粹为编辑方便;文章的 URL、排序和归档全部由 frontmatter 决定,与文件位置无关。frontmatter 示例字段包括:title、date(必填,决定排序,最新在前,并作为 <pubDate>)、authors(对应 docs/blog/authors.yml 的 id,或一个不加链接的普通名字)、categories(分类法,每个分类有自己的 /blog/category/<slug>/ 页与 feed,与 tags 无关)、tags(站点级标签)、summary(列表/RSS 的单行摘要,省略且没有 <!-- more --> 时回退为纯文本截断)、image(特色图片,可为 docs/assets/ 相对路径或完整 URL,同时作为 og:image/Twitter card,除非 ogImage 覆盖)、slug(覆盖 /blog/<slug>/,默认来自文件名)、draft: true(真实构建中完全排除;serve 预览时显示“🚧 Draft”横幅)。普通页面的 frontmatter 键(icon、description、ogImage、toc)同样可用于文章。docs/assets/blog/ 只是文章封面/作者照片的惯例子文件夹,并不强制,任何 docs/assets/** 路径都可用。
作者:docs/blog/authors.yml 可选,每个 id 一条,被文章 authors 列表引用。只有 name 必填。作者只有在至少一篇文章署名后才有自己的 /blog/authors/<id>/ 页。头像按惯例自动识别 docs/assets/blog/authors/<id>.{jpg,jpeg,png,webp,svg};显式 avatar 键总是覆盖它。
导航:只要存在至少一篇非草稿文章,就会自动添加“Blog”导航项(默认追加在最后)。若要显式放置(并抑制自动项),可添加自己的 nav/docs/nav.json 条目并使用裸 url(绕过通常的“路径必须是真实页面”规则)。
订阅源:/blog/feed.xml(RSS 2.0),在 baseURL 为完整 URL(与 sitemap.xml 相同要求)且 blog.feed 不为 false 时写出。每个分类也有 /blog/category/<slug>/feed.xml。两者上限为 blog.feedLimit(默认 25;0 = 不设上限)。配置位于 bxsites.yaml 的 blog 键下。
重新样式化博客:博客页面通过与其它页面完全相同的 layout.bxm/page.bxm 渲染,没有单独的博客主题。生成的标记(文章卡片、meta 行、分页器、作者简介、按年/分类浏览链接)使用固定 CSS 类(blog-post-card、blog-post-meta、blog-post-featured-image、blog-draft-badge、blog-pager、blog-author-profile、blog-archive-links/blog-category-links),可通过 extraCss 重设样式,或通过主题覆写改变结构。文章卡片/分页器/作者简介标记本身由 BlogBuilder.bx 生成,不是从主题模板读取——可用 CSS 重设样式,但不能用自定义模板替换。
二、版本化 添加 docs/versions/ 文件夹;其中每个直接子文件夹都会作为完整自包含的文档树与常规 docs/ 一起构建(常规 docs/ 始终构建为“Latest”)。每个版本文件夹都是普通 docs/ 形状的树——有自己的 index.md、自己的 nav、自己的页面——构建到 site/versions/<name>/,共享项目单一的 bxsites.yaml 配置/主题。
剪切版本: bxSites version:new --name=1.0 它会快照当前 docs/ 树(排除 assets/、versions/、i18n/、blog/——各自是单独加载的树)。典型工作流:完成一次发布的文档,在开始下一版本文档前剪切版本。没有“取消剪切”命令;编辑已剪切版本的页面就是直接编辑文件——page:new/page:rename/post:new 始终只针对主 docs/ 树。
版本名按数字从新到旧排序(2.0 在 10.0 前);一旦存在多个版本,每个主题都会自动渲染版本切换器。sitemap.xml/llms.txt 包含每个版本。
范围之外:搜索按树限定(每个版本单独的 search-index.json),但 pagefind provider 例外,它索引整个构建站点;没有 deprecated/EOL 标记或自定义标签——版本切换器标签始终是其文件夹名。
三、i18n 翻译内容放在 docs/i18n/<code>/,逐页镜像 docs/。<code> 既是文件夹名也是构建出的 URL 前缀(/es/guides/setup/)——仅限字母/数字/连字符(es、pt-BR、zh-Hans)。常规 docs/ 树始终是默认 locale,构建在根路径且无前缀。
脚手架一个 locale: bxSites i18n:new --code=es 它从默认 locale 自己的 index.md 播种。然后在 bxsites.yaml 中注册 label/direction。defaultLocale 只在默认不是英语时才需要设置。没有匹配 locales 条目的文件夹仍会构建(用其裸 code 作为 label)——locales 提供显示元数据,而非开关。
bxSites i18n:status 报告每个 locale 的翻译覆盖情况(每个 locale 在相同相对路径下存在哪些页面)——始终以 0 退出,纯信息性。
未翻译页面:docs/i18n/es/ 中缺失的页面仍会在其预期 URL 构建,显示默认 locale 内容并带有“尚未翻译”提示,不会有 404。每个 locale 的 nav 始终与默认的完全同形。
语言切换器:一旦存在多个 locale 就自动出现,无需选择性开启。
与版本组合:docs/versions/<name>/i18n/<code>/ 镜像该版本自己的结构。切换版本总是落到该版本自己的默认 locale;切换 locale 总是停留在当前版本。
主题外壳(UI 字符串):周边 UI 文本(搜索占位符、“On this page”、404 页等)也按 locale 翻译。de/es/it/ja 内置翻译;其它 code 回退英语。可按键覆写,例如 searchPlaceholder、onThisPage 等。可覆写键包括:searchPlaceholder、searchAriaLabel、searchNoResults、toggleDarkMode、toggleNavigation、toggleSection ({title})、repository、version、language ({label})、onThisPage、editThisPage、downloadMarkdown、lastUpdated、notTranslatedNotice ({locale})、notFoundTitle、notFoundBody、tagsTitle、pageNavigation。
范围之外:博客外壳保持英语;lastUpdated 的日期值不做 locale 格式化;RTL 是基线(镜像布局,少数装饰细节如 admonition 的强调条侧不翻转);无机器翻译——每个 locale 文件都手工编写。custom: 图标和 ::: include 均针对项目共享的 docs/assets/ 解析,与 locale 无关。
四、重定向 页面移动/重命名后保持旧 URL 可用——静态 HTML 存根把过期的书签/搜索结果重定向到新 URL。
按页面:在取代旧 URL 的页面上使用 frontmatter redirect_from,每个条目都是 pretty-URL 段(无前导/尾随斜杠,无扩展名)。范围限定在该页面所属的树(版本页在版本内重定向,locale 页在 locale 内重定向)。
站点范围:bxsites.yaml 的 redirects 数组,用于从不属于某一特定页面的旧 URL。to 是根相对路径(相对 baseURL 解析)或完整 https:// URL。仅适用于主站点树。
bxSites page:rename --from=... --to=... 会在重写所有指向旧路径的相对 Markdown 链接的同时自动打上 redirect_from——始终优先于手工移动文件。
冲突会构建失败而非静默覆盖:重定向的 from 与真实页面冲突,或两个重定向指向同一 from。
范围之外:博客文章没有 redirect_from(改用 redirects 配置条目);没有通配符/模式重定向——每个 from 都是一个精确路径。