Files
OSGKeyboard/OSGKeyboardShared/Typing/RimeSchemaGenerator.swift
Rocky b13a1e904b feat(keyboard): rank pinyin by habit and persist user dictionaries
Erase dialect syllables before abbrev so wom prefers 我们, flush librime
on leave, and add a settings action to forget learned Chinese and English order.
2026-08-14 21:49:03 +08:00

204 lines
6.6 KiB
Swift
Raw Permalink 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.
// RimeSchemaGenerator.swift
// OSGKeyboard · Shared
//
// Generates OSG-owned schemas from public Microsoft/Sogou key maps.
// No GPL Rime schema files are copied or distributed.
import Foundation
public enum RimeSchemaGenerator {
public static func defaultConfiguration() -> String {
"""
# Generated by OSGKeyboard.
config_version: "1.0"
schema_list:
- schema: \(TypingInputSchema.fullPinyin.rawValue)
- schema: \(TypingInputSchema.microsoftDoublePinyin.rawValue)
- schema: \(TypingInputSchema.sogouDoublePinyin.rawValue)
switcher:
caption: 输入方案
hotkeys: []
menu:
# Large page so librime prepares enough candidates for the expand panel.
# The bridge also iterates beyond the current page via candidate_list_*.
page_size: 100
ascii_composer:
good_old_caps_lock: true
switch_key:
Shift_L: noop
Shift_R: noop
Control_L: noop
Control_R: noop
key_binder:
bindings: []
recognizer:
patterns: {}
"""
}
public static func schema(
for inputSchema: TypingInputSchema,
fuzzyPairs: Set<PinyinFuzzyPair>
) -> String {
let alphabet = inputSchema == .fullPinyin
? "zyxwvutsrqponmlkjihgfedcba"
: "zyxwvutsrqponmlkjihgfedcba;"
// Dialect single-letter syllables must be erased before fuzzy/abbrev
// so `n/l` cannot revive 嗯 as `l`, and `wom` cannot exact-match 我呒.
let algebra = dialectEraseRules + fuzzyRules(fuzzyPairs) + algebraRules(for: inputSchema)
let algebraYAML = algebra.map { " - '\($0)'" }.joined(separator: "\n")
return """
# Generated by OSGKeyboard. Do not edit; change settings in the host app.
schema:
schema_id: \(inputSchema.rawValue)
name: \(inputSchema.displayName)
version: "1.0"
author:
- OSGKeyboard contributors
description: |
Commercially permissive OSG schema backed by osg_pinyin.
switches:
- name: ascii_mode
reset: 0
states: [中, 英]
engine:
processors:
- ascii_composer
- recognizer
- key_binder
- speller
- punctuator
- selector
- navigator
- express_editor
segmentors:
- ascii_segmentor
- matcher
- abc_segmentor
- punct_segmentor
- fallback_segmentor
translators:
- punct_translator
- script_translator
speller:
alphabet: "\(alphabet)"
initials: "\(alphabet.replacingOccurrences(of: ";", with: ""))"
delimiter: " '"
algebra:
\(algebraYAML)
translator:
dictionary: osg_pinyin
prism: \(inputSchema.rawValue)
enable_sentence: true
enable_completion: true
enable_user_dict: true
initial_quality: 1.2
punctuator:
half_shape:
",": ""
".": ""
"?": ""
"!": ""
key_binder:
import_preset: default
recognizer:
import_preset: default
"""
}
/// Drop Wu/dialect exact spellings (`呒 m`, `嗯 n/ng`, `噷 hm`) before
/// first-letter abbrev, matching rime-pinyin-simp. Keep the dictionary
/// rows; 嗯 remains reachable as `en`, 呒 as `mu`.
public static let dialectEraseRules: [String] = [
"erase/^hm$/",
"erase/^m$/",
"erase/^n$/",
"erase/^ng$/"
]
/// Rules run against full-pinyin dictionary codes before double-pinyin
/// transforms, so fuzzy pairs work consistently in all three schemas.
public static func fuzzyRules(_ enabled: Set<PinyinFuzzyPair>) -> [String] {
var rules: [String] = []
for pair in PinyinFuzzyPair.allCases where enabled.contains(pair) {
switch pair {
case .zhZ:
rules += ["derive/^zh/z/", "derive/^z([^h])/zh$1/"]
case .chC:
rules += ["derive/^ch/c/", "derive/^c([^h])/ch$1/"]
case .shS:
rules += ["derive/^sh/s/", "derive/^s([^h])/sh$1/"]
case .nL:
rules += ["derive/^n/l/", "derive/^l/n/"]
case .fH:
rules += ["derive/^f/h/", "derive/^h/f/"]
case .anAng:
rules += ["derive/ang$/an/", "derive/an$/ang/"]
case .enEng:
rules += ["derive/eng$/en/", "derive/en$/eng/"]
case .inIng:
rules += ["derive/ing$/in/", "derive/in$/ing/"]
}
}
return rules
}
private static func algebraRules(for schema: TypingInputSchema) -> [String] {
switch schema {
case .fullPinyin:
return [
"derive/^([jqxy])u$/$1v/",
"abbrev/^([a-z]).+$/$1/",
// Two-letter initials; do not add this to double pinyin.
"abbrev/^([zcs]h).+$/$1/"
]
case .microsoftDoublePinyin, .sogouDoublePinyin:
// Microsoft and Sogou's commonly shipped layouts are key-compatible.
// Uppercase markers prevent transforms from matching each other.
return [
"erase/^xx$/",
"derive/^([jqxy])u$/$1v/",
"derive/^([aoe].*)$/o$1/",
"xform/^([ae])(.*)$/$1$1$2/",
"xform/iu$/Q/",
"xform/[iu]a$/W/",
"xform/er$|[uv]an$/R/",
"xform/[uv]e$/T/",
"xform/v$|uai$/Y/",
"xform/^sh/U/",
"xform/^ch/I/",
"xform/^zh/V/",
"xform/uo$/O/",
"xform/[uv]n$/P/",
"xform/(.)i?ong$/$1S/",
"xform/[iu]ang$/D/",
"xform/(.)en$/$1F/",
"xform/(.)eng$/$1G/",
"xform/(.)ang$/$1H/",
"xform/ian$/M/",
"xform/(.)an$/$1J/",
"xform/iao$/C/",
"xform/(.)ao$/$1K/",
"xform/(.)ai$/$1L/",
"xform/(.)ei$/$1Z/",
"xform/ie$/X/",
"xform/ui$/V/",
"derive/T$/V/",
"xform/(.)ou$/$1B/",
"xform/in$/N/",
"xform/ing$/;/",
"xlit/QWRTYUIOPSDFGHMJCKLZXVBN/qwrtyuiopsdfghmjcklzxvbn/"
]
}
}
}