Skip to content

国际化

插件本地化分两个互相独立的层,各有职责:

  1. manifest 字符串namedescription、AI 工具的 description)由宿主根据 manifest.json 中可选的 i18n 块翻译。这些字符串驱动插件市场卡片、工具网格、详情抽屉以及 Agent 工具面板——也就是宿主渲染的所有界面。见下文 manifest 字符串本地化
  2. 插件 UI 字符串(iframe 内部的一切)是插件自己的职责。宿主通过 SDK 推送当前 locale;插件读取它并用自带的消息表翻译。见下文 插件 UI 本地化

无论哪一层,locale 都由宿主统一持有;插件永远不要自带语言切换器。

读取 locale

FengYuClient.ready() 会以一个 Environment 完成,其 locale 字段持有当前活动的 locale 标签(例如 enzh-CN):

js
import { fengyu } from './sdk.js'

const env = await fengyu.ready()
console.log(env.locale)   // → 'zh-CN'

ready() 还会把 locale 应用到文档——document.documentElement.lang 会被设为 env.locale——因此任何 locale 感知的 CSS 或属性选择器都无需额外接线即可工作。

响应变化

如果用户在插件打开期间切换语言,宿主会发出一个 environment 事件。用 client.on('environment', handler) 订阅:

js
const off = fengyu.on('environment', (env) => {
  applyLocale(env.locale)
})
// ...稍后,在销毁时
off()

处理器收到的是完整的、更新后的 Environment,因此重新读取 env.locale(以及 env.theme)并重新渲染即可。

不要在握手之后才订阅

Vue 插件应优先使用 mountFengYuApp;它会先建立环境事件订阅,再等待 ready 握手。如果需要 自行编写启动逻辑,必须保持这个顺序:

ts
const off = client.on('environment', applyEnvironment)
const initial = await client.ready()
applyEnvironment(initial)

宿主可能在 iframe 刚加载时就发送第一条环境事件。如果直到 await client.ready() 之后才注册 监听器,就会产生竞态:初始事件可能丢失,插件会一直停留在兜底的深色/英文界面,即使宿主已经 是浅色/中文。环境事件可能只包含部分字段,因此应先与最近一次状态合并,再更新 Vuetify 和插件 自己的消息表。

manifest 字符串本地化

市场卡片、工具网格、详情抽屉以及 Agent 工具面板所展示的字符串,都是宿主从 manifest.json 读取的。要本地化它们,在 manifest 里加一个可选的 i18n 块,按短 locale 标签(enzh)索引。顶层字段保持英文作为默认值;每个 locale 覆盖字段都各自可选——不翻译的字段直接省略:

json
{
  "id": "fan.summer.excel",
  "name": "Excel Splitter",
  "description": "Split Excel workbooks by sheet, column value, or complex rules",
  "rpc": {
    "methods": {
      "excel_analyze": {
        "description": "Analyze the granted Excel workbook…",
        "inputSchema": { "type": "object", "properties": { "filePath": { "type": "string" } }, "required": ["filePath"] }
      }
    }
  },
  "aiTools": [
    { "name": "excel_analyze", "description": "Analyze the granted Excel workbook…", "method": "excel_analyze", "effect": "read" }
  ],
  "i18n": {
    "zh": {
      "name": "Excel 拆分器",
      "description": "按工作表、列值或复杂规则拆分 Excel 工作簿",
      "aiTools": {
        "excel_analyze": { "description": "分析已授权的 Excel 工作簿……" }
      }
    }
  }
}

对于 locale 为 zh-CN 的请求,查找顺序为:

  1. i18n["zh-CN"] —— 精确标签
  2. i18n["zh"] —— 语言族
  3. 顶层默认值(英文)

任一级别都可以缺失——宿主会依次回退到下一级,因此插件可以只翻译关心的字段,永远不会出现空白。宿主根据请求的 Accept-Language 头选择 locale(前端会自动发送当前 UI 语言)。

此处本地化的内容

  • author —— 品牌标识,不翻译。
  • inputSchema / outputSchema —— 其内嵌的 JSON Schema title/description 保持英文(它们向 LLM 描述工具参数)。
  • AI 工具 description 的覆盖仅用于前端展示。发给 LLM 的字符串始终是顶层英文原文,因此翻译不会影响工具选择质量。

插件 UI 本地化

宿主不翻译插件 iframe UI 内部的字符串。请自带消息表并使用共享响应式运行时,使回退、 插值和 locale 归一化在所有插件中保持一致:

ts
import { createFengYuI18n, mountFengYuApp } from '@infinia/plugin-ui'

const messages = createFengYuI18n({
  en: { title: 'Split complete', pick: 'Choose a file' },
  zh: { title: '拆分完成', pick: '选择文件' },
})

await mountFengYuApp({ root: App, client, onEnvironment: messages.applyEnvironment })
messages.t('title')

当活动 locale 没有翻译时回退到一个默认 locale(通常是 en)。由于宿主设置了 document.documentElement.lang,你随时也可以从 DOM 读取 locale 作为兜底:

js
const locale = document.documentElement.lang  // 'en' | 'zh-CN' | ...

WARNING

不要在你的插件 UI 中添加语言切换器。宿主是 locale 的唯一事实来源;插件级别的切换器会与 App 其余部分失去同步。

下一步

  • UI 微前端——完整的 Environment 结构与 on('environment') 契约。
  • SDK 与 CLI——FengYuClient 参考。

Released under the GPL-3.0 License.