1
This commit is contained in:
@@ -12,6 +12,8 @@
|
||||
@property (nonatomic, strong) NSSet<NSString *> *priorityWords;
|
||||
@property (nonatomic, copy) NSArray<NSString *> *traditionalChineseWords;
|
||||
@property (nonatomic, copy) NSArray<NSString *> *simplifiedChineseWords;
|
||||
@property (nonatomic, strong) NSDictionary<NSString *, NSArray<NSString *> *> *pinyinToTraditionalMap;
|
||||
@property (nonatomic, strong) NSDictionary<NSString *, NSArray<NSString *> *> *bopomofoToChineseMap;
|
||||
@end
|
||||
|
||||
@implementation KBSuggestionEngine
|
||||
@@ -34,6 +36,8 @@
|
||||
_words = [self kb_loadWords];
|
||||
_traditionalChineseWords = [self kb_loadTraditionalChineseWords];
|
||||
_simplifiedChineseWords = [self kb_loadSimplifiedChineseWords];
|
||||
_pinyinToTraditionalMap = [self kb_loadPinyinToTraditionalMap];
|
||||
_bopomofoToChineseMap = [self kb_loadBopomofoToChineseMap];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -206,61 +210,204 @@
|
||||
#pragma mark - Traditional Chinese Pinyin Suggestions
|
||||
|
||||
- (NSArray<NSString *> *)kb_traditionalPinyinSuggestionsForPrefix:(NSString *)prefix limit:(NSUInteger)limit {
|
||||
// 繁体拼音联想:输入拼音,返回繁体中文候选词
|
||||
NSString *lower = prefix.lowercaseString;
|
||||
NSMutableArray<NSString *> *matches = [NSMutableArray array];
|
||||
|
||||
// 这里应该使用拼音到繁体字的映射表
|
||||
// 目前先返回一些常用繁体词作为示例
|
||||
NSArray<NSString *> *directMatches = self.pinyinToTraditionalMap[lower];
|
||||
if (directMatches.count > 0) {
|
||||
[matches addObjectsFromArray:directMatches];
|
||||
}
|
||||
|
||||
for (NSString *key in self.pinyinToTraditionalMap) {
|
||||
if ([key hasPrefix:lower] && ![key isEqualToString:lower]) {
|
||||
NSArray<NSString *> *candidates = self.pinyinToTraditionalMap[key];
|
||||
[matches addObjectsFromArray:candidates];
|
||||
if (matches.count >= limit * 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (matches.count == 0) {
|
||||
return [self kb_fallbackTraditionalSuggestions:lower limit:limit];
|
||||
}
|
||||
|
||||
[matches sortUsingComparator:^NSComparisonResult(NSString *a, NSString *b) {
|
||||
NSInteger ca = self.selectionCounts[a].integerValue;
|
||||
NSInteger cb = self.selectionCounts[b].integerValue;
|
||||
if (ca != cb) {
|
||||
return (cb > ca) ? NSOrderedAscending : NSOrderedDescending;
|
||||
}
|
||||
return [a compare:b];
|
||||
}];
|
||||
|
||||
if (matches.count > limit) {
|
||||
return [matches subarrayWithRange:NSMakeRange(0, limit)];
|
||||
}
|
||||
return matches.copy;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)kb_fallbackTraditionalSuggestions:(NSString *)prefix limit:(NSUInteger)limit {
|
||||
NSMutableArray<NSString *> *matches = [NSMutableArray array];
|
||||
for (NSString *word in self.traditionalChineseWords) {
|
||||
// TODO: 实现拼音匹配逻辑
|
||||
// 这里需要一个拼音库来将输入的拼音转换为繁体字
|
||||
[matches addObject:word];
|
||||
if (matches.count >= limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return matches.copy;
|
||||
}
|
||||
|
||||
#pragma mark - Simplified Chinese Pinyin Suggestions
|
||||
|
||||
- (NSArray<NSString *> *)kb_simplifiedPinyinSuggestionsForPrefix:(NSString *)prefix limit:(NSUInteger)limit {
|
||||
// 简体拼音联想:输入拼音,返回简体中文候选词
|
||||
NSString *lower = prefix.lowercaseString;
|
||||
NSMutableArray<NSString *> *matches = [NSMutableArray array];
|
||||
|
||||
// 这里应该使用拼音到简体字的映射表
|
||||
NSArray<NSString *> *directMatches = self.pinyinToTraditionalMap[lower];
|
||||
if (directMatches.count > 0) {
|
||||
for (NSString *tradChar in directMatches) {
|
||||
NSString *simplified = [self kb_toSimplified:tradChar];
|
||||
if (simplified.length > 0) {
|
||||
[matches addObject:simplified];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (NSString *key in self.pinyinToTraditionalMap) {
|
||||
if ([key hasPrefix:lower] && ![key isEqualToString:lower]) {
|
||||
NSArray<NSString *> *candidates = self.pinyinToTraditionalMap[key];
|
||||
for (NSString *tradChar in candidates) {
|
||||
NSString *simplified = [self kb_toSimplified:tradChar];
|
||||
if (simplified.length > 0) {
|
||||
[matches addObject:simplified];
|
||||
}
|
||||
}
|
||||
if (matches.count >= limit * 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (matches.count == 0) {
|
||||
return [self kb_fallbackSimplifiedSuggestions:lower limit:limit];
|
||||
}
|
||||
|
||||
[matches sortUsingComparator:^NSComparisonResult(NSString *a, NSString *b) {
|
||||
NSInteger ca = self.selectionCounts[a].integerValue;
|
||||
NSInteger cb = self.selectionCounts[b].integerValue;
|
||||
if (ca != cb) {
|
||||
return (cb > ca) ? NSOrderedAscending : NSOrderedDescending;
|
||||
}
|
||||
return [a compare:b];
|
||||
}];
|
||||
|
||||
if (matches.count > limit) {
|
||||
return [matches subarrayWithRange:NSMakeRange(0, limit)];
|
||||
}
|
||||
return matches.copy;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)kb_fallbackSimplifiedSuggestions:(NSString *)prefix limit:(NSUInteger)limit {
|
||||
NSMutableArray<NSString *> *matches = [NSMutableArray array];
|
||||
for (NSString *word in self.simplifiedChineseWords) {
|
||||
// TODO: 实现拼音匹配逻辑
|
||||
[matches addObject:word];
|
||||
if (matches.count >= limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return matches.copy;
|
||||
}
|
||||
|
||||
- (NSString *)kb_toSimplified:(NSString *)traditional {
|
||||
static NSDictionary<NSString *, NSString *> *tradToSimpMap = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
tradToSimpMap = @{
|
||||
@"臺": @"台", @"臺": @"台", @"灣": @"湾", @"語": @"语", @"體": @"体",
|
||||
@"國": @"国", @"學": @"学", @"時": @"时", @"問": @"问", @"見": @"见",
|
||||
@"經": @"经", @"動": @"动", @"長": @"长", @"開": @"开", @"關": @"关",
|
||||
@"無": @"无", @"說": @"说", @"書": @"书", @"電": @"电", @"機": @"机",
|
||||
@"氣": @"气", @"這": @"这", @"們": @"们", @"個": @"个", @"對": @"对",
|
||||
@"來": @"来", @"還": @"还", @"過": @"过", @"會": @"会", @"進": @"进",
|
||||
@"開": @"开", @"頭": @"头", @"點": @"点", @"問": @"问", @"題": @"题",
|
||||
@"變": @"变", @"條": @"条", @"東": @"东", @"車": @"车", @"錢": @"钱",
|
||||
@"門": @"门", @"聽": @"听", @"聲": @"声", @"醫": @"医", @"讓": @"让",
|
||||
@"識": @"识", @"務": @"务", @"農": @"农", @"業": @"业", @"產": @"产",
|
||||
@"黨": @"党", @"歷": @"历", @"史": @"史", @"後": @"后", @"前": @"前",
|
||||
@"強": @"强", @"當": @"当", @"應": @"应", @"從": @"从", @"優": @"优",
|
||||
@"兒": @"儿", @"兩": @"两", @"幾": @"几", @"廣": @"广", @"場": @"场",
|
||||
@"決": @"决", @"許": @"许", @"設": @"设", @"請": @"请", @"論": @"论",
|
||||
@"認": @"认", @"斷": @"断", @"離": @"离", @"須": @"须", @"導": @"导",
|
||||
@"爭": @"争", @"重": @"重", @"輕": @"轻", @"難": @"难", @"極": @"极",
|
||||
@"據": @"据", @"實": @"实", @"際": @"际", @"標": @"标", @"準": @"准",
|
||||
@"確": @"确", @"證": @"证", @"驗": @"验", @"權": @"权", @"規": @"规",
|
||||
@"則": @"则", @"劃": @"划", @"計": @"计", @"劃": @"划", @"術": @"术",
|
||||
@"藝": @"艺", @"術": @"术", @"選": @"选", @"舉": @"举", @"團": @"团",
|
||||
@"結": @"结", @"組": @"组", @"織": @"织", @"義": @"义", @"務": @"务",
|
||||
@"親": @"亲", @"愛": @"爱", @"情": @"情", @"懷": @"怀", @"家": @"家",
|
||||
@"屬": @"属", @"幫": @"帮", @"助": @"助", @"友": @"友", @"誼": @"谊",
|
||||
@"謝": @"谢", @"謝": @"谢", @"對": @"对", @"起": @"起", @"早": @"早",
|
||||
@"安": @"安", @"晚": @"晚", @"請": @"请", @"問": @"问", @"沒": @"没",
|
||||
@"關": @"关", @"係": @"系", @"加": @"加", @"油": @"油", @"台": @"台",
|
||||
@"北": @"北", @"高": @"高", @"雄": @"雄", @"中": @"中", @"南": @"南",
|
||||
@"朋": @"朋", @"友": @"友", @"人": @"人", @"工": @"工", @"作": @"作",
|
||||
@"習": @"习", @"生": @"生", @"活": @"活", @"地": @"地", @"方": @"方",
|
||||
@"法": @"法", @"答": @"答", @"喜": @"喜", @"歡": @"欢", @"想": @"想",
|
||||
@"念": @"念", @"開": @"开", @"心": @"心", @"快": @"快", @"樂": @"乐",
|
||||
@"美": @"美", @"麗": @"丽", @"漂": @"漂", @"亮": @"亮", @"帥": @"帅",
|
||||
@"氣": @"气", @"可": @"可", @"愛": @"爱", @"溫": @"温", @"柔": @"柔"
|
||||
};
|
||||
});
|
||||
|
||||
if (tradToSimpMap[traditional]) {
|
||||
return tradToSimpMap[traditional];
|
||||
}
|
||||
|
||||
NSMutableString *result = [traditional mutableCopy];
|
||||
[tradToSimpMap enumerateKeysAndObjectsUsingBlock:^(NSString *trad, NSString *simp, BOOL *stop) {
|
||||
[result replaceOccurrencesOfString:trad withString:simp options:0 range:NSMakeRange(0, result.length)];
|
||||
}];
|
||||
|
||||
return result.length > 0 ? [result copy] : traditional;
|
||||
}
|
||||
|
||||
#pragma mark - Bopomofo (Zhuyin) Suggestions
|
||||
|
||||
- (NSArray<NSString *> *)kb_bopomofoSuggestionsForPrefix:(NSString *)prefix limit:(NSUInteger)limit {
|
||||
// 注音联想:输入注音符号,返回繁体中文候选词
|
||||
NSMutableArray<NSString *> *matches = [NSMutableArray array];
|
||||
|
||||
// 这里应该使用注音到繁体字的映射表
|
||||
// 注音符号:ㄅㄆㄇㄈㄉㄊㄋㄌㄍㄎㄏㄐㄑㄒㄓㄔㄕㄖㄗㄘㄙ
|
||||
// 韵母:ㄚㄛㄜㄝㄞㄟㄠㄡㄢㄣㄤㄥㄦㄧㄨㄩ
|
||||
// 声调:ˊˇˋ˙
|
||||
for (NSString *word in self.traditionalChineseWords) {
|
||||
// TODO: 实现注音匹配逻辑
|
||||
[matches addObject:word];
|
||||
if (matches.count >= limit) {
|
||||
break;
|
||||
NSArray<NSString *> *directMatches = self.bopomofoToChineseMap[prefix];
|
||||
if (directMatches.count > 0) {
|
||||
[matches addObjectsFromArray:directMatches];
|
||||
}
|
||||
|
||||
for (NSString *key in self.bopomofoToChineseMap) {
|
||||
if ([key hasPrefix:prefix] && ![key isEqualToString:prefix]) {
|
||||
NSArray<NSString *> *candidates = self.bopomofoToChineseMap[key];
|
||||
[matches addObjectsFromArray:candidates];
|
||||
if (matches.count >= limit * 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (matches.count == 0) {
|
||||
return [self kb_fallbackTraditionalSuggestions:prefix limit:limit];
|
||||
}
|
||||
|
||||
[matches sortUsingComparator:^NSComparisonResult(NSString *a, NSString *b) {
|
||||
NSInteger ca = self.selectionCounts[a].integerValue;
|
||||
NSInteger cb = self.selectionCounts[b].integerValue;
|
||||
if (ca != cb) {
|
||||
return (cb > ca) ? NSOrderedAscending : NSOrderedDescending;
|
||||
}
|
||||
return [a compare:b];
|
||||
}];
|
||||
|
||||
if (matches.count > limit) {
|
||||
return [matches subarrayWithRange:NSMakeRange(0, limit)];
|
||||
}
|
||||
return matches.copy;
|
||||
}
|
||||
|
||||
@@ -281,7 +428,6 @@
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)kb_loadSimplifiedChineseWords {
|
||||
// 加载简体中文常用词
|
||||
return @[
|
||||
@"你好", @"谢谢", @"对不起", @"再见", @"早安",
|
||||
@"晚安", @"请问", @"不好意思", @"没关系", @"加油",
|
||||
@@ -293,4 +439,96 @@
|
||||
];
|
||||
}
|
||||
|
||||
#pragma mark - Pinyin & Bopomofo Map Loading
|
||||
|
||||
- (NSDictionary<NSString *, NSArray<NSString *> *> *)kb_loadPinyinToTraditionalMap {
|
||||
NSString *path = [[NSBundle mainBundle] pathForResource:@"pinyin_to_traditional" ofType:@"json"];
|
||||
if (!path) {
|
||||
NSLog(@"[KBSuggestionEngine] pinyin_to_traditional.json not found, using empty map");
|
||||
return @{};
|
||||
}
|
||||
|
||||
NSData *data = [NSData dataWithContentsOfFile:path];
|
||||
if (!data) {
|
||||
NSLog(@"[KBSuggestionEngine] Failed to read pinyin_to_traditional.json");
|
||||
return @{};
|
||||
}
|
||||
|
||||
NSError *error = nil;
|
||||
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
|
||||
if (error || ![json isKindOfClass:NSDictionary.class]) {
|
||||
NSLog(@"[KBSuggestionEngine] Failed to parse pinyin_to_traditional.json: %@", error);
|
||||
return @{};
|
||||
}
|
||||
|
||||
NSDictionary *mappings = json[@"mappings"];
|
||||
if (![mappings isKindOfClass:NSDictionary.class]) {
|
||||
NSLog(@"[KBSuggestionEngine] Invalid mappings in pinyin_to_traditional.json");
|
||||
return @{};
|
||||
}
|
||||
|
||||
NSMutableDictionary<NSString *, NSArray<NSString *> *> *result = [NSMutableDictionary dictionary];
|
||||
[mappings enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
|
||||
if ([obj isKindOfClass:NSArray.class]) {
|
||||
NSMutableArray<NSString *> *chars = [NSMutableArray array];
|
||||
for (id item in (NSArray *)obj) {
|
||||
if ([item isKindOfClass:NSString.class]) {
|
||||
[chars addObject:item];
|
||||
}
|
||||
}
|
||||
if (chars.count > 0) {
|
||||
result[key] = [chars copy];
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
NSLog(@"[KBSuggestionEngine] Loaded %lu pinyin mappings", (unsigned long)result.count);
|
||||
return [result copy];
|
||||
}
|
||||
|
||||
- (NSDictionary<NSString *, NSArray<NSString *> *> *)kb_loadBopomofoToChineseMap {
|
||||
NSString *path = [[NSBundle mainBundle] pathForResource:@"bopomofo_to_chinese" ofType:@"json"];
|
||||
if (!path) {
|
||||
NSLog(@"[KBSuggestionEngine] bopomofo_to_chinese.json not found, using empty map");
|
||||
return @{};
|
||||
}
|
||||
|
||||
NSData *data = [NSData dataWithContentsOfFile:path];
|
||||
if (!data) {
|
||||
NSLog(@"[KBSuggestionEngine] Failed to read bopomofo_to_chinese.json");
|
||||
return @{};
|
||||
}
|
||||
|
||||
NSError *error = nil;
|
||||
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
|
||||
if (error || ![json isKindOfClass:NSDictionary.class]) {
|
||||
NSLog(@"[KBSuggestionEngine] Failed to parse bopomofo_to_chinese.json: %@", error);
|
||||
return @{};
|
||||
}
|
||||
|
||||
NSDictionary *mappings = json[@"mappings"];
|
||||
if (![mappings isKindOfClass:NSDictionary.class]) {
|
||||
NSLog(@"[KBSuggestionEngine] Invalid mappings in bopomofo_to_chinese.json");
|
||||
return @{};
|
||||
}
|
||||
|
||||
NSMutableDictionary<NSString *, NSArray<NSString *> *> *result = [NSMutableDictionary dictionary];
|
||||
[mappings enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
|
||||
if ([obj isKindOfClass:NSArray.class]) {
|
||||
NSMutableArray<NSString *> *chars = [NSMutableArray array];
|
||||
for (id item in (NSArray *)obj) {
|
||||
if ([item isKindOfClass:NSString.class]) {
|
||||
[chars addObject:item];
|
||||
}
|
||||
}
|
||||
if (chars.count > 0) {
|
||||
result[key] = [chars copy];
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
NSLog(@"[KBSuggestionEngine] Loaded %lu bopomofo mappings", (unsigned long)result.count);
|
||||
return [result copy];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user