pi-localize1.1.0
Localize pi's interface — command descriptions and panel descriptions — and generate language packs on demand with /localize.
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
pi-localize

把 pi 界面的英文换成你的语言。只做两件事:
/命令背后的那行描述(含参数补全里的描述);- 设置 / 选择器 / 子菜单里,条目标题下方的描述(含第三方命令与插件)。
语言包不随包发布:你选一次语言,pi 用你自己的 agent、在你自己的机器上生成它。没有运行时翻译, 除了你那次模型调用发送的内容以外,什么都不外发。
覆盖范围
| 钩子 | 翻译什么 | 永不触碰 |
|---|---|---|
ctx.ui.addAutocompleteProvider |
/ 命令描述、参数补全描述 |
命令名(label)、文件补全 |
活的组件树 + 实例 render |
面板 / 选择器 / 子菜单条目的 description | label、values、currentValue、聊天正文、页脚 |
对话框标题、空状态、状态标签、键位提示行暂不翻译:它们属于规划中的类别,加类别不需要动运行时骨架
(见 docs/adr/0001-scope-and-categories.md)。
需求
- 支持扩展的 pi(只用文档化钩子:
addAutocompleteProvider、registerCommand、getCommands)。 - Node 20+。
@earendil-works/pi-tui—— 由 pi 在运行时提供,所以只声明为 peer dependency,绝不打包。- 可选:
pi-subagents(或任何注册subagents/run命令的扩展)。装了它,生成本语言包会切成并行批次。
安装
pi install npm:pi-localize # 从 npm
pi install git:github.com/JBP-1994/pi-localize # 从 git
pi install /absolute/path/to/pi-localize # 从本地 checkout
装完 /reload 或重启 pi,然后跑 /localize。
使用
/localize
用 ↑ / ↓ 选语言,Enter 确认,Esc 退出。每一行都写清了会发生什么:英文是「源语言 —— 无需翻译」,
没有语言包的是「还没有语言包 —— 选中即开始生成」。
| 状态 | 结果 |
|---|---|
| 该语言还没有包 | agent 生成一个:lang/<code>.mjs 加 lang/ui/<code>.mjs,写好 localize.config.json,下一轮生效,不用重启。 |
| 已有语言包 | 二选一:启用当前的(直接用) 或 全量重建。 |
首次生成的成本:这是一次真实的模型调用,花你自己的额度。按当前 pi 界面 + 已装插件,大概要翻几百条文案。
pi 或插件更新后,再跑 /localize → 全量重建:每次都把整包重译一遍 —— 不读旧包、不复用旧译文
(见 docs/adr/0006-full-rebuild-every-time.md)。记忆会让一个包里同时存在两套措辞,而模型漏抄一条就
静默丢一条。代价是每次都要翻整包:本机 344 条约 9 批子代理、几分钟。
配置
| 项 | 位置 |
|---|---|
| 当前语言 | 包目录下的 localize.config.json:{ "locale": "ja" }。默认 en,即不翻译。 |
| 换个配置位置 | PI_LOCALIZE_CONFIG=/path/to/localize.config.json |
命令快照(供 scan / check 用) |
系统临时目录的 pi-localize-commands.json;PI_LOCALIZE_COMMANDS 可覆盖。 |
改配置在 session_start 时生效,所以 /reload 就够,不用重启。
语言
可选语言表在 lang/languages.mjs,只收 9 门常用语言(判据见
ADR 0005)。英文的地区变体故意不收:它们会和源语言完全一样。
表外的语言仍可选:选择器最后一行「输入其他语言」接受任意 BCP-47 代码,名称与旗帜由 Intl 现算。
加一门语言:在 lang/languages.mjs 加一行(code、旗帜、名称),然后跑 /localize 选它。
贡献语言包:语言包默认 gitignore。要贡献:
git add -f lang/<code>.mjs lang/ui/<code>.mjs # 越过 .gitignore
npm test # 结构、占位符、术语
npm run test:guards # 每条键都能重新生成,且没有行标题混入
语言包怎么生成
三条渠道的候选合成一个提示词,agent 只翻译、不判断。候选永远是整包(没有翻译记忆,见 ADR 0006),扩展自己从不调用模型。
| 渠道 | 来源 | 强项 | 局限 |
|---|---|---|---|
| 静态 UI 字段 | 源码 registerCommand / new SettingsList / items: [ 区域里 description / hint 等字段与 key 成对出现的位置 |
形态规范,自带归属 | 看不到藏在函数里的文案 |
| 形态候选 | 文件名像面板模块的文件里,形态像文案的字面量 | 能捡到少见的字段名和函数拼出来的文案 | 选不准;与其它候选一样一律翻译,不逐条判断(ADR 0007) |
| 运行时命令 | 会话内 pi.getCommands() + pi 内置命令表 |
命令描述的唯一权威来源 | 只有命令,没有面板文案 |
命令行工具读不到活的命令列表,改读扩展写的会话快照。
改译文:重新生成,不要手改
语言包是生成物,不是源码。手改的值下一次 /localize 就没了,而且会掩盖造成缺口的扫描 bug。
译文不对 → 跑 /localize → 全量重建。某条文案干脆生成不出来,那是扫描器的 bug(缺一条渠道或一个候选
位置),修工具而不是补包。类别与键的约束:
- 键是冻结的:
COMMANDS键是命令名,ARGS键是命令/参数,DESCRIPTIONS键是英文原文。只翻值。 - 占位符保留:
{name}。 - 术语保持英文:清单在
lib/terms.mjs的KEEP_ENGLISH。 - 注释与文档用中文,值和注释同语言。
- 本扩展自己的文案放
lang/ui/<code>.mjs,绝不进 pi 语言包。
排错
什么都没翻译。 看 localize.config.json 的当前语言(或跑 /localize 看哪一行有 ✔)。英文就是「不翻译」,
也是默认值;语言包缺失同样回退到它,而不是报错。
有个别文案还是英文。 两种常见原因:那条文案属于暂不翻译的类别(对话框标题、空状态、状态标签);或者它
是插件在你上次生成之后新加的。后者跑 /localize → 全量重建。
译文读着别扭。 别改包,跑 /localize → 全量重建。
想要的语言不在列表里。 见 语言。
开发
没有构建步骤:pi 用 jiti 直接加载 index.ts,扩展是带 JSDoc 类型的普通 ESM。
pi -e ./index.ts # 试用一次,不安装
pi install -l . # 把 checkout 注册为项目包(.pi/settings.json)
pi install "$PWD" # 注册到本机
会话里 /reload 就能吃到代码改动。
npm test # 有语言包的检查 + 全新安装(零包)状态
npm run test:packs # 快循环:只跑有语言包的检查
npm run test:zero-pack # 模拟刚 pi install 的状态
npm run test:guards # 每条 DESCRIPTIONS 键都能重新生成?没有行标题混入?(读本机插件)
npm run lint # ESLint(只 lint *.mjs,最小规则集)
npm run format:check # Prettier(代码与配置;*.md 与生成物不在范围)
npm run scan # 已装插件的 UI 文案覆盖率
node scan.mjs --json # 机器可读的缺口清单
node scan.mjs --all # 忽略覆盖率(重建视图)
check.mjs 有两种状态,都必须绿:
- 有语言包 —— 你的 checkout 通常有
lang/<code>.mjs,翻译相关的断言会跑; - 零包 —— 刚
pi install的样子(只有lang/languages.mjs和lang/ui/en.mjs)。依赖语言包的断言 跳过 而不是失败。npm test会在临时目录里造出这个状态再跑一遍自检。
自检还会真的跑一遍扩展接线(假的 pi + session_start / turn_start):只 import 是看不出回调里写错的。
目录结构
index.ts 接线:状态 + 钩子 + session/turn 事件
lib/categories.mjs 翻译类别注册表(一类文案 = 一张表 + 一个渠道 + 一个应用点)
lib/packs.mjs 语言包加载:唯一知道包形状与磁盘布局的地方
lib/tables.mjs 语言包 → 翻译逻辑(纯函数)
lib/hooks/autocomplete.mjs "/" 面板命令 / 参数描述
lib/hooks/panels.mjs 活的组件树 + 实例 render(只改 description)
lib/commands/localize.mjs /localize:选择器、切换、生成
lib/plugin-scan.mjs 三条覆盖渠道
lib/command-list.mjs 命令列表会话快照(给 scan / check 用)
lib/translate-prompt.mjs 生成「翻译这个包」的提示词(正文英文,注释中文)
lib/terms.mjs 保持英文的术语
lib/language-picker.mjs 选择器组件
lang/languages.mjs 可选语言
lang/<code>.mjs pi 界面语言包,按用户生成(开发参考:zh-CN)
lang/ui/<code>.mjs 本扩展自己的界面文案(源:en)
check.mjs scan.mjs 自检 / 覆盖率扫描
eslint.config.mjs ESLint flat config(最小集,无插件)
.prettierrc.json Prettier 选项(tabWidth 1,与现有代码一致)
test/all.mjs npm test 的入口
test/zero-pack.mjs 在模拟的全新安装上跑 check.mjs
test/pack-guards.mjs 重建安全:每条键都能重新发现?没有行标题?
CONTEXT.md 领域术语表
docs/adr/ 关键决定
设计说明
- 不 patch 类。 pi 用 jiti 加载扩展,
import到的 pi-tui 类和运行中 bundle 用的不是同一个类对象。 只能改活对象:组件树。 - 不做整屏行替换。 那会改到聊天正文、页脚和其它组件。作用域只能是面板自己的输出。
- 只改
description。label/values/currentValue被查表、搜索、循环使用。 - 缺语言包是正常状态,不是错误:扩展照常加载、回退英文,
/localize生成缺的部分。 - 包形状只定义一次(
lib/categories.mjs+lib/packs.mjs),运行时、scan.mjs、check.mjs共用。
隐私
- 没有遥测、没有网络请求;扩展自己从不联系模型。
/localize只是把提示词发进你当前会话,走你自己配置的 provider 和账号。- 命令快照只含命令名与描述,落在系统临时目录。
许可
MIT