Files
qingjian/docs/design/aux-code.md
T
yenharvey d54dbf57e4 feat(platform): 随包笔画表按每字首笔给表外的词算码
- 笔画表只收了基础词库的词,自造词、导入词库与学到的词在辅码态会被藏起来;装随包 stroke.qj 时挂上与 dict-convert 打包同一条词码规则(每字首笔,四字以上取前三字与末字),表外的词现算
- 首右不做:sysrf.cn 的私有方案,字根图无授权、自己生成也对不上官方码;设计文档记下理由
- 待办记下「整句 + 辅码」,先不做
2026-10-20 14:39:25 +08:00

15 KiB
Raw Permalink Blame History

辅码

issue #8:联想里没有的词要翻很多页;加辅码(拼音后补码筛选)能把第一次打句子的效率提上来,选过一次的词靠既有学习在下次纯拼音就往前排。 本文是这份需求的设计来源:要做什么、为什么这么做、数据从哪来、许可怎么算。实现按 docs/plan/todo.md 排期。

术语

词 意思
辅码 拼音之后键入的、用来缩小候选范围的码。形态是「拼音 + 触发键 + 码」。
触发键 进入辅码态的那个键。可配置,缺省 ;。
码段 输入串里触发键之后的字母,与拼音段分开记账、分开画。
码表 「词 → 码」的映射数据,导入与管理的单位。
原生表 随包分发、开箱可用的码表。v1 只有笔画。
引导导入 不随包带表,由用户自己从官方渠道取得码表后一键导入。第三方形码(小鹤形、自然码形)走这条路。

辅码的语义(Core)

触发

  • 配置项 [general] aux_code_key,缺省 ;;也想跟雾凇(反引号 + 偏旁部首拼音)或万象(/ 前置引导符)对齐的用户改这一个键即可。
  • 优先级规则(与现有行为共存):当前 preedit 能完整切分成音节、且敲下的字符是配置的触发键 → 进辅码态;否则维持现有语义(; 仍按中文标点转全角、不会转全角的符号仍进英文直输段——会转全角的标点先把高亮候选上屏再补标点——翻页键仍是翻页键)。
  • 微软 / 搜狗 / 紫光双拼里 ; 是 ing 的韵母键:能构成合法音节就当韵母,否则当触发键。x; 是 xing(; 被韵母吃掉),再敲一个 ; 才进辅码态。

过滤

  • 词级(含单字):码只筛词候选,不改选词逻辑,也不改整句转换。
  • 逐键即筛:码段每多一个字母就重新筛一次,不要求敲满该词的码位;敲到满意即可上屏。
  • 无码词在辅码态直接隐藏:码表没覆盖的词不出候选;码段删空后停在辅码态——; 仍在、候选全部回来,空码段再按一次退格才退出([general] aux_code_keep_empty,缺省开;关掉则删空即回纯拼音态)。
  • 筛空时候选窗口收起,只剩拼音行。

学习(issue 里「下一次不用辅码也能联想出来」就落在这)

不新造机制:辅码态选中的词按输入串记进 user-choices.tsv(词级排序里同输入串选过的优先),并计入词频与个人 n-gram。 下次同样的拼音,这个词自然往前排。也就是说辅码只需解决「第一次怎么少翻页」。

与整句、简拼的关系

v1 只支持全拼 + 辅码、双拼 + 辅码。简拼 + 辅码、整句转换 + 辅码(形码逐字约束进 Viterbi 词图)都留到后续,见「后续与不做」。

码表

格式:Rime .dict.yaml

沿用已有的 Rime 词库导入传统(crates/qingjian-dictionary/src/import/rime.rs 已能把 .dict.yaml 转成 .qj),码表用它同一套外形:

  • 头部 --- … ... 是 meta(name / version / sort),正文每行 词\t码[\t权重],# 是注释。
  • columns 必须解析:缺省是 text / code / weight 三列,但真实存在 columns: [text, weight] 的纯词表——照缺省解析会把词频当码,静默写坏数据。解析按 columns 列表的下标定位。
  • import_tables 表示跨文件合表(一本码表由 base + stem + extended 拼成):导入时一并读入,否则数据不完整。
  • 「有词无码」的行要报错或警告,不能像词库导入那样静默跳过(码表少一行就是少一个词可用,用户得知道)。
  • 导入完成给一份统计:读入多少条、多少词有码、多少词无码、跳过多少行。

索引:独立码索引,两段式查询

码表不能复用词库的存储:

  • 词库的 Slot{text_start, frequency, text_len, reserved} 没有码字段;
  • 键空间会串台——de / he / bu / ge 这类两位码同时是合法拼音音节,同一个索引里既当码又当拼音,约束互相污染。

所以码表落成自己的索引(码 → 词集合),查询是两段式:先用拼音段查词库得到候选集合,再用码段在该索引里取交集、按码命中重排。码表文件与词库文件一样进用户数据目录,单独管理。

原生表:只带笔画

第三方形码表都不随包,理由在「许可」一节(首右同理:sysrf.cn 的私有方案,字根图无授权,自己生成也对不上官方码,2026-10 决定不做)。随包的只有笔画:

  • 键位:h 横、s 竖、p 撇、n 捺(含点)、z 折——与大陆厂商(搜狗 / QQ)的笔画输入一致,键盘手位在拼音区。
  • 取码:单字「前 4 笔 + 末笔」;词组每字首笔(二字 2 码、三字 3 码、四字以上 = 前三字首笔 + 末字首笔)。
  • 归一化:点与捺合并为 n、提并入横。
  • 数据管线:以 CNS11643 全字庫「筆順資料」(CNS_strokes_sequence.txt,官方定义 1 橫 / 2 豎 / 3 撇 / 4 點 / 5 折)作字形来源,经官方 CNS→Unicode 对照表映射;其上叠加大陆序覆盖表——CNS 是台湾标准,与大陆规范按部件成片不一致(艹:台 4 笔首笔竖 / 陆 3 笔首笔横;辶 4/3、阝 3/2),一级字抽样 299 字里 31 例笔画数不符(10.4%)。混合表不可接受:rime-stroke 就是双轨(花 = 艹 4 画台湾序、节 = 艹 3 画大陆序),大陆用户按学校笔顺敲不出 花 这类字。
  • 工具:tools/dict-convert 新增 stroke 子命令,输入 --cns-seq(笔顺表)、--cns-map(CNS→Unicode 对照)、--cns-count(可选,与笔画数自洽过滤,建议 |d| ≤ 1)、--filter(字表白名单,取通用规范字表)、--prc-rules(大陆序覆盖),输出 字\t[1-5]{n}(8,105 字约 40–80 KB)。原始 zip 不入库,走 data/ 的既有惯例只留生成产物。

导入

  • 「导入码表…」:接受 Rime .dict.yaml,走系统打开文件对话框 / 文件选择器;失败时在窗口底部红字一行说明(例如「这个文件里没有识别到码表(需要词 + 码两列)」)。
  • 导入完成给一份统计:读入多少条、多少词有码、多少词无码、跳过多少行。
  • 引导导入的文案要写清:码表由用户自己取得(例如小鹤形的官方码表),青简不随包分发第三方形码表。

设置界面(两个壳都要做)

同一组设置项在两个平台的设置界面各出现一次,写进同一份 config.toml(配置文件是唯一事实源):

设置项 macOS(偏好设置,AppKit) Windows(设置程序,WinUI 3)
触发键 录制按钮,缺省 ; 录制控件,同一缺省
候选上显示码 勾选框,缺省不勾 同左
辅码方案 下拉框,同一时间只用一套(几套叠着筛,同一个字母在不同方案里意思不同,筛不干净;Rime 的万象、雾凇也是选定一套) 同左
码表列表 随包笔画不可移除;导入的显示名字 · 条数 · 许可,可移除 同结构的一页
导入码表… 系统打开文件对话框 系统文件选择器

设置项命名、文案与失败提示两平台保持一致;Core 只提供配置项与状态,界面归壳。

候选窗口与输入串

  • 输入串(preedit):码段用淡色 + 下划线画法、; 不突出——与既有段样式同一套视觉词汇(敲的拼音正常色、剩余拼音画淡、被纠错改掉的画删除线)。Core 把码段作为新的段类型(MarkedKind)给出,两个壳各自落到自己的载体:macOS 走行内 marked text 与窗口拼音行,Windows 走 protocol 的 PreeditSegment(TSF DLL 交给系统)。
  • 候选:辅码态只出命中码的词(无码词隐藏);敲码筛空时候选窗口收起,只剩拼音行。
  • 显示码开关打开时:码用方括号紧跟在候选词后面(1 鹤[rbm] crane,渲染器 Row.code,小字淡色):码是词本身的属性,不进右侧的 annotation;译文仍是候选词的单条 annotation(docs/design/architecture.md)。开着时不用进辅码态:纯拼音打字与辅码态空码段也把词的首条码挂上(边打边认码),筛码时显示命中的那条。竖排会比纯译文挤、横排更难放下,这是这个开关的已知代价(缺省关)。
  • 窗口布局、视觉层级、翻页键等沿用 docs/design/candidate-ui.md;绘制归各壳(macOS 自绘 NSPanel,Windows Server 自绘候选窗与状态条)。渲染器 spike(crates/qingjian-render,分支 renderer-spike)落地后两个平台都只贴图。

配置

键 缺省 说明
[general] aux_code_key ";" 触发键,可改反引号或 /
[general] aux_code_show false 候选上是否显示码(与译文拼接;开着时未进辅码态也显示词的首条码)
[general] aux_code_keep_empty true 码段删空后是否留在辅码态(空码段再按一次退格才退出);false 删空即回拼音态
[aux_code] enabled false 辅码总开关:关掉整条辅码线(; 回到原生行为);开着但没有可用码表也不进辅码态
[aux_code] scheme "" 用哪一套(码表文件名或辅码方案 id),同一时间只用一套;空着或找不到时用第一套没被关掉的(随包笔画在最前)
[aux_code] disabled [] 老配置逐张关掉的码表;现在只在 scheme 没选时用来跳过,设置程序不再写

分层与落点

层 放什么
qingjian-core 码段解析(触发键消歧、码段与拼音段分开记账)、码索引的两段式查询、过滤与重排、MarkedKind 新段;码表查询走 trait,不认识文件
qingjian-dictionary 码表解析(.dict.yaml + columns / import_tables 规则)、独立码索引与落盘、导入统计
qingjian-platform 配置项、码表与方案的目录与开关
平台壳 macOS:apps/macos(IMK 偏好设置页、候选窗自绘);Windows:apps/windows/settings(WinUI 3 设置页)与 apps/windows/server(自绘候选窗与状态条)。两边都只做两件事:把码段画出来、把设置界面画出来

平台层不出现排序逻辑、词库访问或翻译调用(docs/design/architecture.md 的架构约束)。

测试与评测

  • Core 单测:码段解析与消歧(x; = xing、再 ; 才进辅码)、逐键即筛、无码词隐藏、码段删空两态(缺省停在辅码态、aux_code_keep_empty = false 回拼音态)、码表 columns / import_tables / 「有词无码」三类边界。
  • 学习闭环:辅码态选词后,同输入串(纯拼音)首选命中——用 apps/cli --replay 在现有输入日志上验。
  • 评测:导入时报告「多少词有码 / 无码」,用码表覆盖率与 --eval-text 的整句命中率当两把尺子。
  • 排序、整句、纠错的改动一律先跑 --replay 与 --eval-text(仓库既有规矩)。
  • Windows:本机只 cargo check --target x86_64-pc-windows-gnu;真编译与真机测试在 Windows 机器上做(Server + TSF DLL + 设置程序)。
  • macOS:IMK 不能 cargo run 验证,要 apps/macos/scripts/bundle.sh --install 装到本机;osascript 往 TextEdit 发按键读回文本可做端到端。

许可

  • 项目是 GPL-3.0-or-later(LICENSE、Cargo.toml)。
  • 小鹤形(小鹤音形的形码部分):官网声明「未经本站允许,不得用于任何商业目的」+ All Rights Reserved,无开放许可,不能随包;要内置须先取得作者书面许可。
  • 自然码形:作者坚持商业发行,形码无授权记录,不能随包。
  • 笔画(CNS11643 筆順資料):台湾数位发展部开放数据,许可为「政府資料開放授權條款第 1 版」或「OFL-1.1」二选一,可再分发、含商用,需署名——随包时在「关于」页与数据目录署名,沿用 assets/levels、assets/glossary 的做法。
  • Make Me a Hanzi / hanzi-writer-data:PRC 笔顺,只作开发期差异定位,不随包(其笔顺数据是 Arphic Public License)。
  • 第三方码表不随包:码表由用户自己从官方渠道取得后导入;这是工程偏好(文件小、可控、不带上游维护负担),不是许可禁令。
  • 用户导入第三方码表是用户自己的行为,不构成再分发。

后续与不做

平台范围:本设计对两个现役平台一视同仁——macOS(apps/macos)与 Windows(apps/windows/{server,tsf,settings})的壳与设置界面都要接;逻辑全在 Core,Linux 未开工,落地时走同一套通路,不需要改 Core。

落地分工(按 docs/contributing.md「一个 PR 只做一件事、只碰一个平台」):三步可以独立合,只有一个平台机器的人也能完成自己那一步——

步 范围 验证方式
1 Core:码表解析、独立码索引、码段与两段式查询(单独一个 PR,不碰壳) cargo test -p qingjian-core / -p qingjian-dictionary、apps/cli 手测、--replay
2 Windows 壳:Server / TSF 的 preedit 分段与候选绘制、apps/windows/settings(WinUI 3)的「辅码」页 Windows 机器上 cargo check --target x86_64-pc-windows-gnu + 真机部署(Server + DLL + 设置程序)
3 macOS 壳:IMK 的 marked text 分段、候选窗绘制、偏好设置页 macOS 机器上 bundle.sh --install + 真机输入验证

第 1 步是后两步共同的前置;第 2、3 步之间没有依赖——先合哪一个都行,只有一个平台的贡献者只做自己那一步,另一平台由别的贡献者或维护者补。

后续(未定,需要时另开设计):简拼 + 辅码;整句转换 + 辅码(形码逐字约束进词图);码表导入后的生效时机与热切换。

不做:五笔原生表;社区码表在线下载 / 同步;内置任何第三方形码表。

参考

  • docs/design/candidate-ui.md:候选窗口与按键约定(本文的码段与注记沿用它的视觉词汇)。
  • docs/design/architecture.md:Core 与平台层的划分、翻译是单条 annotation 的约束。
  • 渲染器 spike(分支 renderer-spike 上的 crates/qingjian-render 与 docs/design/rendering.md):候选窗一帧 + 主题 → 位图,各平台只贴图。
  • docs/notes/crate-notes.md:各 crate / app / tool 的实现要点(改了实现要同步那里)。
  • docs/plan/todo.md:排期与进度。