Files
paseo/docs/website-i18n.md
T
Mohamed Boudra d87998bc8c feat(website): serve paseo.sh in Simplified Chinese
Every page of the website now has a Simplified Chinese version under
/zh-CN: the interface, docs, blog posts, comparison pages, legal pages,
all changelog releases and the homepage mockup. English URLs are unchanged.

The URL alone decides the language. There is no browser-language redirect
and no stored preference, because either would have to redirect the
unprefixed English URLs that people share and search engines index.
Internal links carry the page's language instead, and hreflang alternates
point Chinese search results at /zh-CN.

Translated headings pin the English anchor with {#id}, so section links
work in both languages. content-translations.test.ts fails when a page
has no translation or a translation's headings drift from the English.
Changelog releases and plugin listings without a translation render
whole in English under a notice, so a release never waits on one.

Plugin manifests and the registry gain optional translations and
OVERVIEW.zh-CN.md fields; older clients and registries ignore them.
2026-10-19 08:30:45 +02:00

5.5 KiB

Website translations

paseo.sh is served in English and Simplified Chinese (zh-CN). This doc covers the website in packages/website. App UI translations are in i18n.md.

URLs own the language

  • English pages keep their unprefixed paths. Every other language lives under its tag: /docs/cli and /zh-CN/docs/cli are the same page. /en/... is not a page.
  • The server renders the language in the URL. There is no browser-language detection or redirect, so a shared link always opens in the language it was shared in.
  • The language selector moves to the same page in the other language and keeps the query and hash. Every internal link on a page carries the page's language, so the choice sticks while the reader navigates. An explicit English URL always renders English.
  • The choice is not stored. A stored choice could only take effect by redirecting unprefixed URLs, which are the English pages. First-party links into the site carry the reader's language instead: README.zh-CN.md links to /zh-CN/....
  • Routes live under src/routes/{-$locale}/. Build internal links with localizePath or useLocalizePath from ~/i18n. Links inside Markdown are localized by DocsMarkdown. Files, .md sources, llms.txt and API endpoints keep one path for every language (isPagePath).
  • Give each page's head the language-neutral path through pageMeta (src/meta.ts). It writes the self canonical, the hreflang alternates with English as x-default, and og:locale. The sitemap in vite.config.ts lists the same alternates.

Where copy lives

Content English Chinese
Interface copy src/i18n/resources/en.json src/i18n/resources/zh-CN.json
Docs public-docs/<page>.md public-docs/<page>.zh-CN.md
Blog posts posts/<slug>.md posts/<slug>.zh-CN.md
Comparison pages src/content/alternatives/<slug>.md src/content/alternatives/<slug>.zh-CN.md
Legal pages legal/<page>.md legal/<page>.zh-CN.md
Changelog CHANGELOG.md CHANGELOG.zh-CN.md, see release.md
Plugin listings the plugin's paseo-plugin.json and OVERVIEW.md translations and OVERVIEW.zh-CN.md, see public-docs/plugins/publishing.md

en.json types the keys, so typecheck fails when zh-CN.json misses one. A translation file is grouped with its English original by name (src/markdown-content.ts). A translation without an English original fails the build.

Every first-party page has a translation. The changelog and plugin listings are the exceptions: a release or listing without one renders whole in English under a notice. Pages never mix languages paragraph by paragraph. Navigation order, categories and slugs always come from the English file's frontmatter.

Editing a translated doc

Edit the English file and its .zh-CN.md in the same PR, and add the .zh-CN.md with any new page. Code blocks, commands, flags, config keys and links are copied verbatim, so code-only changes are a copy. Never delete a translation to get around an edit, and never leave one that describes behavior the English no longer has.

Translated headings pin the English anchor with a trailing {#id}, so /zh-CN/docs/cli#running-agents lands on the same section as the English link:

## 运行 Agent {#running-agents}

:::example[回复 Slack 提及 {#answer-a-slack-mention}]

When you rename an English heading, change the {#id} in the translation to the new slug. src/content-translations.test.ts fails when a docs, blog, comparison or legal page has no translation, and when a translation's headings differ from the English in count, level, order or anchor. It cannot tell whether the prose under a heading is current. The .md source endpoints serve files verbatim, so {#id} shows there as written.

Chinese style

  • Keep code, commands, file paths, IDs, brand and product names, and command output in English.
  • Keep these terms in English: Agent, worktree, daemon, Provider, PR, diff, Composer, Hub, workflow, heartbeat, slug.
  • Translate these: workspace 工作区, host 主机, relay 中继, plugin 插件, skill 技能, review 审查, schedule 定时任务, orchestration 编排, configuration 配置, prompt 提示词, subagent 子 Agent.
  • Quote app UI labels with the app's Chinese wording from packages/app/src/i18n/resources/zh-CN.ts, such as 设置 → 插件. Labels the app has not translated stay in English, as do labels in Hub, GitHub, Slack and Discord. Add the English label in parentheses only where readers search for it, such as an error message.
  • Put a space between Chinese and Latin text or numbers: 启动 Agent, 共 3 个.
  • Bold that ends in full-width punctuation does not render when Chinese text follows it directly: **注意:**这里 shows the asterisks. Move the punctuation outside the bold (**注意**:这里).