输入关键词,开始搜索

↑↓ 选择↵ 打开esc 关闭
沉浸

界面语言

界面语言

Vergil 的界面文案支持中文和英文切换。改一行配置,主题自己渲染的所有文字就会跟着变。

它管什么,不管什么

这一点先说清楚,避免误解。

会跟着切换的,是主题自带的界面文案:

  • 导航与结构:上一篇 / 下一篇、目录、面包屑、分页、跳过导航
  • 侧边栏:站点信息、最近更新、精选文章、相关文章、标签、热力图
  • 交互组件:搜索框、主题切换(含 11 个配色方案的名字)、浮动音频播放器、站点助理
  • 整页:404、工具箱、数据统计、简历、四季相册
  • 指令渲染出的文字:倒计时的天时分秒、日历的上月下月、OKR 的状态、plan 看板的列名与空态
  • <html lang> 属性和日期格式

不会跟着切换的,是属于你的东西:

  • 你写的文章、文档、图文动态、相册说明
  • src/data/config/ 里你填的内容:站点标题、导航菜单的标签、hero 文案、订阅框文字

这是有意为之。主题不去猜你的内容该是什么语言,你用什么语言写,它就原样呈现。

配置

在 src/data/config/identity.ts 里:

TypeScript
export const siteInfo = {    title: 'Vergil',    locale: 'zh-CN',   // 改成 'en' 即切换为英文界面    // ...};

目前支持 'zh-CN' 和 'en' 两个值。填了不认识的值会自动退回 'zh-CN',站点不会因此构建失败。

改完要重启开发服务器

astro.config.mjs 在启动时会读取一次字典,把文案传给内容指令插件。改完 locale 之后,pnpm dev 需要重启才能看到指令部分的变化,页面热更新不足以覆盖。

日期格式

日期不是硬编码的,会跟随 locale 走。同一篇文章的发布时间,在两种语言下分别是:

locale显示
zh-CN2026年5月30日
enMay 30, 2026

文章卡片上的相对时间(今天 / 昨天 / 3 天前)同理。

加一种新语言

字典在 src/i18n/ 下,三个文件:

文件作用
zh-CN.ts中文基准。所有 key 以它为准
en.ts英文。类型声明为 typeof zhCN
index.ts读配置选字典,导出 locale 和 t

加一种语言,比如日语:

第一步,复制 en.ts 为 ja.ts,把值翻译过去。类型声明保持不变:

TypeScript
import type zhCN from './zh-CN'; const ja: typeof zhCN = {    // ...}; export default ja;

这个类型声明很关键。少写一个 key、多写一个 key、或者把函数写成字符串,TypeScript 都会报错,不会等到页面上才发现某处是空白。

第二步,在 index.ts 的 dictionaries 里登记:

TypeScript
import ja from './ja'; const dictionaries = { 'zh-CN': zhCN, en, ja } as const;

第三步,把 siteInfo.locale 改成 'ja'。

在自己写的组件里用

如果你新增了组件,里面的文案也想跟着切换:

Astro
---import { t } from '../../i18n';--- <span>{t.nav.prev}</span><h3>{t.sidebar.featured}</h3>

带参数的 key 是函数,直接调用:

Astro
<p>{t.page.series.postsCount(12)}</p><!-- zh-CN: 共 12 篇文章    en: 12 posts -->

t 在构建期就确定了,没有运行时开销,也有完整的类型提示——key 写错编辑器会直接标红。

客户端脚本里的文案

浏览器里运行的脚本拿不到 t,需要在渲染时把文案传进去。

内联脚本用 define:vars:

Astro
<script is:inline define:vars={{ i18n: { empty: t.common.empty } }}>    if (!items.length) el.textContent = i18n.empty;</script>

打包脚本(没有 is:inline,或者里面有 import)不能用 define:vars,改成把文案挂在 DOM 属性上再读:

Astro
<div id="feed" data-i18n-empty={t.common.empty}></div> <script>    const el = document.getElementById('feed');    el.textContent = el.dataset.i18nEmpty;</script>
未知标题
00:00
00:00
播放列表