语言包

Hermes 内置 17 种 UI 语言(display.language)。该列表是可插拔的:一个语言包就是一个插件(或你 Hermes 主目录中的一个文件夹),用于新增语言或覆盖既有语言的措辞——一次覆盖 Python 侧(CLI 审批提示、网关回复、工具动词、提示)、hermes --tui 界面和桌面应用。

一个包翻译的是静态 UI 文本。agent 的回复、工具输出、日志和斜杠命令名称保持原样;要让 agent 自己用另一种语言回答,请在提示词中说明。

语言从何而来

Hermes 查找一个字符串时,会自上而下依次经过以下各层,取第一个命中:

  1. 插件语言包——每个声明了 provides_locales 的已安装插件(两个包覆盖同一 key 时,后加载者胜出)。
  2. 你的覆盖层——<HERMES_HOME>/locales/<lang>.yaml(按 profile:每个 profile 主目录有自己的 locales/ 文件夹)。
  3. 内置——随 Hermes 分发的 locales/<lang>.yaml 文件。
  4. 同样这三层用于英文,最后才是原始 key。

每一层都可以是部分的:一个包或覆盖层只需包含它要改动的 key。

hermes config set display.language <id> 接受这些层中任一层提供的 id。未知 id 会被拒绝,并列出可用语言:

$ hermes config set display.language pl
✗ display.language 的未知语言 'pl'。可用:en, af, ar, de, es, ...
  安装语言包插件(hermes plugins install <包名>),或放入 <HERMES_HOME>/locales/<id>.yaml 以添加。

使用一个包

hermes plugins install https://github.com/teknium1/hermes-lang-pl   # 或目录名
hermes config set display.language pl

重启正在运行的网关/TUI,使其加载该包。TUI 和桌面端的语言切换器会以母语名称(endonym)列出内置语言、你的覆盖层语言和每个已安装包。.env 中的 HERMES_LANGUAGE 仍优先于 display.language。

编写一个包

一个包不需要任何 Python 代码。最小布局:

hermes-lang-pl/
  plugin.yaml
  locales/
    pl.yaml            # 核心:Python 侧字符串(approval.*, gateway.*, cli.*, display.*, slash.*, tips.*, ...)
    pl.tui.yaml        # 可选:hermes --tui 字符串
    pl.desktop.yaml    # 可选:Hermes 桌面端字符串

plugin.yaml:

name: hermes-lang-pl
version: 1.0.0
description: Hermes 波兰语语言包
provides_locales:
  - id: pl            # 小写 BCP-47 风格标签:pl, pt-br, zh-hant
    endonym: Polski   # 显示在语言切换器中
    rtl: false        # 从右向左书写时为 true(翻转桌面端布局)

provides_locales 条目也可以是纯 id(- pl);此时 endonym 默认为该 id。当清单声明了 provides_locales,插件加载器会自动注册每个 locales/<lang>[.tui|.desktop].yaml。

文件与 key

  • pl.yaml(核心) 镜像内置 locales/en.yaml 的结构。嵌套 YAML 展开为点分 key(approval.denied、gateway.goal_cleared)。复制 en.yaml,翻译值,删掉你不想覆盖的部分。
  • pl.tui.yaml / pl.desktop.yaml 镜像 TUI 和桌面应用的英文目录。key 集合导出到 Hermes 仓库的 locales/_keys.tui.json 和 locales/_keys.desktop.json,校验器即据此检查。
  • YAML 值必须是文本。数字、列表、true/false 或空值都会被拒绝。
  • 切勿把 YAML 保留字(on、off、yes、no)用作 key。

占位符

  • 核心(Python)字符串使用与英文完全一致的命名占位符:"⏳ 正在排空 {count} 个活跃 agent……"。保留英文值中的每个 {name};占位符缺失或拼写错误会使 Hermes 对该 key 回退到未翻译字符串。
  • TUI 和桌面端条目若其英文值是一个函数(接受参数),则在 YAML 中写成带位置占位符的字符串:"{0}/{1} 个会话"。

校验

hermes plugins validate ./hermes-lang-pl

校验器会检查:每个声明的 id 都有 locales/<id>.yaml;每个文件可解析且仅含文本(非文本值是错误);并对该界面英文目录中不存在的 key 发出警告(列出它们——无害但不起作用)。.tui.yaml / .desktop.yaml 的检查在对应 _keys.*.json 导出存在时运行。

从 Python 注册(可选)

已有 Python 代码的插件可自行注册目录:

from pathlib import Path

def register(ctx):
    here = Path(__file__).parent
    ctx.register_locale("pl", here / "locales" / "pl.yaml", endonym="Polski")
    ctx.register_locale("pl", {"approval": {"denied": "      ✗ Odrzucono"}})          # dict,嵌套或扁平
    ctx.register_locale("pl", here / "locales" / "pl.tui.yaml", surface="tui")
    ctx.register_locale_dir(here / "locales")                                         # 一次性全部注册

register_locale(lang, source, *, endonym=None, rtl=False, surface="core") 接受 YAML 路径或映射;surface 为 core、tui 或 desktop。插件卸载时注册随之撤销,且绝不改动 display.language。

无需插件的个人覆盖

把一个部分文件放进你的 Hermes 主目录:

# ~/.hermes/locales/en.yaml —— 只放你想改的 key
approval:
  denied: "      ✗ 不行。"

覆盖层仅作用于该 profile(命名 profile 为 ~/.hermes/profiles/<name>/locales/)。为 Hermes 未内置的语言提供覆盖层文件,也会让该语言变得可选。编辑会在下次启动或下次 display.language 变更时读取。

故障排查

  • display.language 的未知语言 ……——包未安装或未启用(hermes plugins list),或 provides_locales 中的 id 与文件名不匹配(pl 需要 locales/pl.yaml)。
  • 某个已翻译字符串仍显示英文——该 key 不在英文目录中(运行 hermes plugins validate;会列出未知 key),或占位符集合与英文不一致。
  • 语言已列出,但 TUI/桌面端仍是英文——该包没有 .tui.yaml / .desktop.yaml;这些界面会渲染英文加上包所提供的内容。对于 16 种内置语言,TUI 在 Hermes 树中自带 locales/<lang>.tui.yaml(桌面端把翻译打包在应用内),因此为这些语言之一制作的包只需包含它想覆盖的 key。