Files
msime-windows/scripts/build-dictionary.py
T
Lu Fan(SonnyCalcr) 3cd429b19d feat(server): 加入 S 模式符号输入与符号混输 (#1047)
符号数据改用 rime-wanxiang 的符号表与 typst/codex 符号名,源数据放在
msime-dictionary 的 sources/symbols/,替换原来的 piliapp 符号表。

- 建库:新增 symbols_db.py,生成表情面板用的 symbol_catalog(中文分类)和
  S 模式、符号混输用的 symbol_code;剔除 wanxiang-unrenderable.txt 列出的
  Windows 自带字体显示不了的符号,以及与 ① ❶ 看不出区别的 Dingbats 带圈数字
- 新增 scripts/check-symbol-rendering.py,用 Windows 字体生成上面的剔除清单
- Shift+S 进入符号模式,按万象分组码(xl、sx、szq)、数字读音(yi → 一 ① ⑴)
  或符号名(alpha、infinity)检索
- 符号混输:整串精确等于编码时混入,首个紧跟首选;读得成拼音的编码只混数字
  序号写法;双拼先按方案换算成全拼再查(小鹤 sj → san → ③)
- 表情面板符号页:中文分类,搜索把整词命中的符号列在「最佳匹配」
- 配置:utility.symbol_mode(默认开)、general.symbol_mixed_input(默认关)

Refs #795
2026-10-18 02:08:39 +08:00

109 lines
5.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""从 msime-dictionary 的文本源数据本地构建 Windows 端词库,写到 MetasequoiaImeDict/out/。
读取 msime-dictionary checkout 的 sources/ 与 custom/,加上 scripts/dictionary_build/inputs.lock.json 固定的
仓外文件(msime 的快捷短语、读音纠错、emoji/颜文字表,ECDICT,SCOWL,rime-jp_sela),产出:
msime.db 全拼表(含地名补充、自定义词条、读音纠错)、86 五笔、快捷短语、japanese_lexicon
english.db 英文候选、中英双向释义(ECDICT)、sources/cn-en/ 的中译英释义(全部义项)、custom/translations.txt 覆盖
others.db emoji、颜文字、符号(sources/symbols/ 的万象符号表,剔除 wanxiang-unrenderable.txt 列出的)
dict_japanese.dat 日文整句模型(sources/japanese/ 的 Mozc 词库)
以及 Mozc 与 SCOWL 的声明、dictionary-manifest.json、SHA256SUMS.txt、local-dictionary.json
构建规则移植自 msime 的 Rust 构建器(crates/dict-builder)。--compare-release 把结果与官方 Release 逐表对比。
替换 out/ 前,如果里面是产品锁钉住的旧词库,先整份备份到 MetasequoiaImeDict/out.bak/。
用法:
python scripts/build-dictionary.py # 默认读与本仓同级的 msime-dictionary
python scripts/build-dictionary.py --dictionary D:/msime-dictionary
python scripts/build-dictionary.py --compare-release dict-v2.0.13 # 构建后与官方 Release 逐表对比
python scripts/build-dictionary.py --verify # 只校验 out/ 是否仍是本脚本构建的结果
python scripts/build-dictionary.py --if-stale # 源数据或构建脚本没变就跳过(installer\test.ps1 用)
构建 others.db 需要 pypinyin(python -m pip install pypinyin)。
"""
from __future__ import annotations
import argparse
import os
import sqlite3
import subprocess
import sys
import tempfile
import time
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from dictionary_build import build, compare, inputs # noqa: E402
def default_dictionary() -> Path:
if os.environ.get("MSIME_DICTIONARY"):
return Path(os.environ["MSIME_DICTIONARY"])
return build.ROOT.parent / "msime-dictionary"
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument("--dictionary", type=Path, default=None,
help="msime-dictionary checkout,默认取环境变量 MSIME_DICTIONARY,否则是本仓同级的 msime-dictionary")
parser.add_argument("--msime-resources", type=Path, default=None,
help="改用这个目录下的 msime resources/dictionary-sources 文件(本地修改用),不按锁校验")
parser.add_argument("--compare-release", metavar="TAG", help="构建后与这个 dict-v* Release 逐表对比,不一致时失败")
parser.add_argument("--verify", action="store_true", help="只校验 out/ 是否仍是本脚本构建的结果")
parser.add_argument("--if-stale", action="store_true",
help="out/ 已由当前 msime-dictionary 提交和当前构建脚本构建、且工作区干净时跳过,否则重新构建")
args = parser.parse_args()
if args.verify:
marker = build.verify_out()
state = ",有未提交改动" if marker.get("dirty") else ""
commit = marker["custom_dictionary_commit"][:12] or "不是 Git 工作区"
print(f"词库已就绪:{build.OUT}(msime-dictionary {commit}{state})")
return
dictionary = (args.dictionary or default_dictionary()).resolve()
if not (dictionary / "sources").is_dir() or not (dictionary / "custom").is_dir():
raise SystemExit(f"{dictionary} 不是 msime-dictionary checkout(没有 sources/ 和 custom/),用 --dictionary 指定")
commit, dirty = inputs.git_state(dictionary)
print(f"msime-dictionary:{dictionary}({commit[:12] or '不是 Git 工作区'}{',有未提交改动' if dirty else ''})")
if args.if_stale and not args.compare_release:
reason = build.stale_reason(commit, dirty or args.msime_resources is not None)
if reason is None:
print(f"词库已是最新:{build.OUT}")
return
print(f"重新构建词库:{reason}")
mismatched = inputs.check_upstream(dictionary)
if mismatched:
print(f"提示:{len(mismatched)} 个文件与 upstream.lock.json 记录的不同(本地改过的上游文件或生成表):"
f"{', '.join(mismatched[:5])}{' ...' if len(mismatched) > 5 else ''}")
source = inputs.fetch(dictionary, build.CACHE, args.msime_resources)
started = time.perf_counter()
build.DICT_ROOT.mkdir(parents=True, exist_ok=True)
# 整套构建并检查通过之前不动已有的 out/。
with tempfile.TemporaryDirectory(dir=build.DICT_ROOT, prefix="incoming-") as temporary:
staging = Path(temporary)
build.build_all(source, staging)
build.check_outputs(staging)
build.write_manifests(staging, commit, dirty or args.msime_resources is not None, source.msime_commit)
if args.compare_release:
problems = compare.compare(staging, args.compare_release, build.DICT_ROOT / "cache", commit,
source.msime_commit)
for problem in problems:
print(f" 不一致:{problem}")
if problems:
raise SystemExit(f"与 {args.compare_release} 不一致,out/ 未替换")
build.install(staging)
build.verify_out()
print(f"词库构建完毕({time.perf_counter() - started:.0f}s):{build.OUT}")
if __name__ == "__main__":
try:
main()
except (OSError, ValueError, KeyError, sqlite3.Error, subprocess.CalledProcessError) as error:
raise SystemExit(f"失败:{error}") from error