多语言
组织多语言内容、路由、界面文案、搜索范围和 SEO 多语言链接。
多语言站点不只是换掉几条界面文案。svedocs 会为每个页面记录所属语言,让路由、导航、搜索结果、Ask AI 引用、HTML 语言属性和 SEO 多语言链接保持一致。
配置语言
TypeScriptsvedocs.config.tsimport { defineConfig } from 'svedocs/config';export default defineConfig({ site: { name: 'Acme Docs', url: 'https://docs.acme.com' }, i18n: { defaultLocale: 'en-US', prefixDefaultLocale: false, locales: [ { code: 'en-US', label: 'English', path: 'en', hreflang: 'en-US', dir: 'ltr' }, { code: 'zh-Hans', label: '中文', path: 'zh', hreflang: 'zh-CN', dir: 'ltr' }, { code: 'ar', label: 'العربية', path: 'ar', hreflang: 'ar', dir: 'rtl' } ] }});
这些字段分别表示内部标识、公开 URL 和标准语言标签:
| 字段 | 作用 |
|---|---|
code | 稳定的内部标识,会写入页面、搜索记录和 AI 过滤条件。 |
label | 语言切换器显示的名称,建议使用该语言自己的写法。 |
path | 用于发现内容和生成路由的单个目录名和 URL 路径段,默认等于 code。 |
hreflang | SEO 元数据和文档 lang 属性使用的 BCP 47 语言标签,默认等于 code。 |
dir | 文档方向,可选 ltr 或 rtl,默认是 ltr。 |
把 code 和 path 分开后,可以用 zh-Hans 作为稳定标识,同时提供更短的 /zh 路由。以后即使公开 URL 发生变化,搜索过滤条件和已存数据也不必跟着修改。
svedocs 会在发现内容前检查语言配置。以下情况会直接报错:code、path 或语言标签重复,defaultLocale 指向未配置的语言,路径为空、包含多段或不适合放入 URL,路径与 /docs 冲突,以及 messages 使用了未配置的语言。重复检查不区分大小写,因此不会受到宿主文件系统影响。
组织内容
在两个内容根目录下,都使用语言的 path 作为第一层目录:
Textcontent/├── docs/│ ├── en/│ │ ├── index.md│ │ └── guides/deploy.md│ ├── zh/│ │ ├── index.md│ │ └── guides/deploy.md│ └── ar/│ ├── index.md│ └── guides/deploy.md└── pages/ ├── en/index.md ├── zh/index.md └── ar/index.md
当 defaultLocale: 'en-US' 且 prefixDefaultLocale: false 时,路由映射如下:
| 源文件 | 路由 | 语言代码 | 翻译分组 |
|---|---|---|---|
content/docs/en/index.md | /docs | en-US | /docs |
content/docs/zh/index.md | /docs/zh | zh-Hans | /docs |
content/docs/ar/guides/deploy.md | /docs/ar/guides/deploy | ar | /docs/guides/deploy |
content/pages/en/index.md | / | en-US | / |
content/pages/zh/index.md | /zh | zh-Hans | / |
如果要求每个公开 URL 都带语言路径,设置 prefixDefaultLocale: true。上面的英文路由会变成 /docs/en 和 /en。
默认语言文件也可以直接放在 content/docs 和 content/pages 下。根目录文件与默认语言目录二选一,不要同时维护,否则对应文件会生成重复路由。使用彼此镜像的语言目录更容易比对,也方便日后调整 URL 前缀策略。
除了正文,也要翻译 frontmatter。标题、描述、navTitle、关键词和有意义的图片替代文本都会出现在导航、搜索或页面元数据中。各语言目录里的对应文件应保持相同的相对路径,这样 svedocs 才能把它们识别为同一页面的不同译本。
本地化站点外壳
i18n.messages 可以按语言覆盖默认主题文案。没有提供的值会使用英文基础文案。
TypeScriptsvedocs.config.tsexport default defineConfig({ theme: { nav: [ { label: 'Docs', labelKey: 'nav.docs', href: '/docs' }, { label: 'Guides', labelKey: 'nav.guides', href: '/docs/guides' } ], home: { primaryAction: { label: 'Read the guides', labelKey: 'home.readGuides', href: '/docs/guides' } } }, i18n: { defaultLocale: 'en', locales: ['en', 'zh'], messages: { en: { 'nav.guides': 'Guides', 'home.readGuides': 'Read the guides' }, zh: { 'nav.guides': '指南', 'home.readGuides': '阅读指南', 'search.placeholder': '搜索文档', 'ask.label': '问 AI' } } }});
内建文案键覆盖导航、搜索、Ask AI、目录、文章操作、代码工具、首页卡片、错误页和渲染错误。项目也可以添加自定义键。导航、社交链接、页脚和首页操作项都支持 labelKey;brand.labelKey、home.kickerKey 和 home.visual.altKey 的用法相同。普通文本字段仍然是默认值,在没有对应翻译时显示。
自定义组件可以从 SvedocsThemeContext 同时读取内部语言代码和 HTML 语言标签:
Svelte<script lang="ts"> import type { SvedocsThemeContext } from 'svedocs/theme/types'; export let context: SvedocsThemeContext;</script><p lang={context.languageTag} dir={context.locale?.dir ?? 'ltr'}> {context.t('home.readGuides')}</p>
过滤条件和持久化状态使用 context.localeCode;HTML 属性和按语言格式化数据时使用 context.languageTag。
自动匹配多语言链接
对于品牌、顶部导航、社交链接、页脚、首页操作和 Markdown 正文中的站内链接,svedocs 会查找当前语言下的对应页面。因此中文页面上的 /docs/guides 配置链接,在译文存在时会变成 /docs/zh/guides。
语言切换器使用相同的页面映射。如果当前页面没有某种语言的译文,对应选项会被禁用,不会让用户误以为译文存在。其他站内链接会在当前语言内容缺失时指向默认语言页面。用户直接请求缺少译文的本地化 URL 时,edge 构建会返回临时 HTTP 307 重定向;static 和 SPA 构建会为每个可确定的兜底路径预渲染等效的跳转文档,因此即使静态主机本身不返回 307,浏览器仍会进入默认语言的 canonical 页面。默认语言页面也不存在时仍返回 404。
镜像文件之间的相对 Markdown 链接会被解析为实际生成的路由,链接中的 .md、.mdx 或 .svx 扩展名也会被正确处理:
Markdown继续阅读[部署指南](../guides/deploy.md)。
不带语言的绝对链接 /docs/guides/deploy 也会按相同规则本地化。明确写出的语言路由(例如 /docs/zh/guides/deploy)会尊重作者指定的语言;query 和标题 fragment 会完整保留。
自定义 Svelte 或主题组件可以使用 LocalizedLink,在浏览器端复用同一套规则:
Svelte<script lang="ts"> import { LocalizedLink } from 'svedocs/theme'; import type { SvedocsThemeContext } from 'svedocs/theme/types'; export let context: SvedocsThemeContext;</script><LocalizedLink {context} href="/docs/guides/deploy">部署</LocalizedLink>
搜索与 Ask AI
页面和章节搜索记录都会带上 metadata.locale = code。使用默认的 scope: 'current' 时,搜索与 Ask AI 请求会包含当前语言代码,不会混入其他语言的结果。只有确实需要跨语言结果时,才把服务的 scope 设为 all。
托管索引必须把 locale 保留为可过滤字段。内建的 Algolia、Typesense、Cloudflare AI Search 和本地适配器都会自动传递这个过滤条件。
SEO 与文档语言
设置 site.url 后,svedocs 才能生成绝对 canonical URL 和对应的多语言 URL。每组翻译会生成:
- 当前路由的 canonical URL。
- 只指向真实存在翻译的双向
hreflang链接。 - 翻译存在时,指向默认语言页面的
x-default。 - 站点地图中使用相同映射的多语言链接。
- Open Graph 当前语言和真实存在的其他语言。
- JSON-LD
inLanguage。 - 来自
languageTag的<html lang>和来自dir的<html dir>。
缺失的译文不会出现在 Open Graph 或 hreflang 多语言声明中。自定义根布局应调用 createPageMetadata(config, page, pages),以便根据完整页面列表生成 Open Graph 的语言映射。
检查翻译覆盖率
可以在项目里启用检查,也可以通过 CLI 临时开启:
TypeScriptsvedocs.config.tsexport default defineConfig({ checks: { translations: true }});
Shellsvedocs check --translationssvedocs check --translations --strict
检查会按相对路径匹配公开文档和独立页面,并报告缺少哪种已配置语言。隐藏页面不参与检查;使用 --strict 时,警告也会让命令失败。
每次新增、重命名或删除译文后都应运行检查。发布前还应至少测试每种语言的一个路由、语言切换器中的一个缺失译文,以及一个对应语言的 404 路由。