2
This commit is contained in:
@@ -8,8 +8,10 @@
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSInteger, KBSuggestionEngineType) {
|
||||
KBSuggestionEngineTypeLatin = 0, // 拉丁字母(英语、葡萄牙语、印尼语)
|
||||
KBSuggestionEngineTypeLatin = 0, // 拉丁字母(英语)
|
||||
KBSuggestionEngineTypeSpanish, // 西班牙语
|
||||
KBSuggestionEngineTypePortuguese, // 葡萄牙语
|
||||
KBSuggestionEngineTypeIndonesian, // 印度尼西亚语
|
||||
KBSuggestionEngineTypePinyinSimplified, // 简体拼音
|
||||
KBSuggestionEngineTypePinyinTraditional, // 繁体拼音
|
||||
KBSuggestionEngineTypeBopomofo // 注音(繁体)
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
@property (nonatomic, strong) NSDictionary<NSString *, NSArray<NSString *> *> *pinyinToTraditionalMap;
|
||||
@property (nonatomic, strong) NSDictionary<NSString *, NSArray<NSString *> *> *bopomofoToChineseMap;
|
||||
@property (nonatomic, copy) NSArray<NSString *> *spanishWords;
|
||||
@property (nonatomic, copy) NSArray<NSString *> *portugueseWords;
|
||||
@property (nonatomic, copy) NSArray<NSString *> *indonesianWords;
|
||||
@end
|
||||
|
||||
@implementation KBSuggestionEngine
|
||||
@@ -40,6 +42,8 @@
|
||||
_pinyinToTraditionalMap = [self kb_loadPinyinToTraditionalMap];
|
||||
_bopomofoToChineseMap = [self kb_loadBopomofoToChineseMap];
|
||||
_spanishWords = [self kb_loadSpanishWords];
|
||||
_portugueseWords = [self kb_loadPortugueseWords];
|
||||
_indonesianWords = [self kb_loadIndonesianWords];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -50,6 +54,10 @@
|
||||
switch (self.engineType) {
|
||||
case KBSuggestionEngineTypeSpanish:
|
||||
return [self kb_spanishSuggestionsForPrefix:prefix limit:limit];
|
||||
case KBSuggestionEngineTypePortuguese:
|
||||
return [self kb_portugueseSuggestionsForPrefix:prefix limit:limit];
|
||||
case KBSuggestionEngineTypeIndonesian:
|
||||
return [self kb_indonesianSuggestionsForPrefix:prefix limit:limit];
|
||||
case KBSuggestionEngineTypePinyinTraditional:
|
||||
return [self kb_traditionalPinyinSuggestionsForPrefix:prefix limit:limit];
|
||||
case KBSuggestionEngineTypePinyinSimplified:
|
||||
@@ -163,6 +171,10 @@
|
||||
self.engineType = KBSuggestionEngineTypeLatin;
|
||||
} else if ([engineTypeString isEqualToString:@"spanish"]) {
|
||||
self.engineType = KBSuggestionEngineTypeSpanish;
|
||||
} else if ([engineTypeString isEqualToString:@"portuguese"]) {
|
||||
self.engineType = KBSuggestionEngineTypePortuguese;
|
||||
} else if ([engineTypeString isEqualToString:@"indonesian"]) {
|
||||
self.engineType = KBSuggestionEngineTypeIndonesian;
|
||||
} else if ([engineTypeString isEqualToString:@"pinyin_traditional"]) {
|
||||
self.engineType = KBSuggestionEngineTypePinyinTraditional;
|
||||
} else if ([engineTypeString isEqualToString:@"pinyin_simplified"]) {
|
||||
@@ -539,35 +551,13 @@
|
||||
#pragma mark - Spanish Suggestions
|
||||
|
||||
- (NSArray<NSString *> *)kb_spanishSuggestionsForPrefix:(NSString *)prefix limit:(NSUInteger)limit {
|
||||
NSString *lower = prefix.lowercaseString;
|
||||
NSMutableArray<NSString *> *matches = [NSMutableArray array];
|
||||
|
||||
for (NSString *word in self.spanishWords) {
|
||||
if ([word hasPrefix:lower]) {
|
||||
[matches addObject:word];
|
||||
if (matches.count >= limit * 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSArray<NSString *> *matches = [self kb_suggestionsFromWordList:self.spanishWords
|
||||
prefix:prefix
|
||||
limit:limit];
|
||||
if (matches.count == 0) {
|
||||
return [self kb_latinSuggestionsForPrefix: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;
|
||||
return matches;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)kb_loadSpanishWords {
|
||||
@@ -607,4 +597,136 @@
|
||||
return result.count > 0 ? [result copy] : [self.class kb_defaultWords];
|
||||
}
|
||||
|
||||
#pragma mark - Portuguese Suggestions
|
||||
|
||||
- (NSArray<NSString *> *)kb_portugueseSuggestionsForPrefix:(NSString *)prefix limit:(NSUInteger)limit {
|
||||
NSArray<NSString *> *matches = [self kb_suggestionsFromWordList:self.portugueseWords
|
||||
prefix:prefix
|
||||
limit:limit];
|
||||
if (matches.count == 0) {
|
||||
return [self kb_latinSuggestionsForPrefix:prefix limit:limit];
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)kb_loadPortugueseWords {
|
||||
NSString *path = [[NSBundle mainBundle] pathForResource:@"portuguese_words" ofType:@"json"];
|
||||
if (!path) {
|
||||
NSLog(@"[KBSuggestionEngine] portuguese_words.json not found, using default words");
|
||||
return [self.class kb_defaultWords];
|
||||
}
|
||||
|
||||
NSData *data = [NSData dataWithContentsOfFile:path];
|
||||
if (!data) {
|
||||
NSLog(@"[KBSuggestionEngine] Failed to read portuguese_words.json");
|
||||
return [self.class kb_defaultWords];
|
||||
}
|
||||
|
||||
NSError *error = nil;
|
||||
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
|
||||
if (error || ![json isKindOfClass:NSDictionary.class]) {
|
||||
NSLog(@"[KBSuggestionEngine] Failed to parse portuguese_words.json: %@", error);
|
||||
return [self.class kb_defaultWords];
|
||||
}
|
||||
|
||||
NSArray *wordsArray = json[@"words"];
|
||||
if (![wordsArray isKindOfClass:NSArray.class]) {
|
||||
NSLog(@"[KBSuggestionEngine] Invalid words array in portuguese_words.json");
|
||||
return [self.class kb_defaultWords];
|
||||
}
|
||||
|
||||
NSMutableArray<NSString *> *result = [NSMutableArray array];
|
||||
for (id item in wordsArray) {
|
||||
if ([item isKindOfClass:NSString.class]) {
|
||||
[result addObject:item];
|
||||
}
|
||||
}
|
||||
|
||||
NSLog(@"[KBSuggestionEngine] Loaded %lu Portuguese words", (unsigned long)result.count);
|
||||
return result.count > 0 ? [result copy] : [self.class kb_defaultWords];
|
||||
}
|
||||
|
||||
#pragma mark - Indonesian Suggestions
|
||||
|
||||
- (NSArray<NSString *> *)kb_indonesianSuggestionsForPrefix:(NSString *)prefix limit:(NSUInteger)limit {
|
||||
NSArray<NSString *> *matches = [self kb_suggestionsFromWordList:self.indonesianWords
|
||||
prefix:prefix
|
||||
limit:limit];
|
||||
if (matches.count == 0) {
|
||||
return [self kb_latinSuggestionsForPrefix:prefix limit:limit];
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)kb_loadIndonesianWords {
|
||||
NSString *path = [[NSBundle mainBundle] pathForResource:@"indonesian_words" ofType:@"json"];
|
||||
if (!path) {
|
||||
NSLog(@"[KBSuggestionEngine] indonesian_words.json not found, using default words");
|
||||
return [self.class kb_defaultWords];
|
||||
}
|
||||
|
||||
NSData *data = [NSData dataWithContentsOfFile:path];
|
||||
if (!data) {
|
||||
NSLog(@"[KBSuggestionEngine] Failed to read indonesian_words.json");
|
||||
return [self.class kb_defaultWords];
|
||||
}
|
||||
|
||||
NSError *error = nil;
|
||||
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
|
||||
if (error || ![json isKindOfClass:NSDictionary.class]) {
|
||||
NSLog(@"[KBSuggestionEngine] Failed to parse indonesian_words.json: %@", error);
|
||||
return [self.class kb_defaultWords];
|
||||
}
|
||||
|
||||
NSArray *wordsArray = json[@"words"];
|
||||
if (![wordsArray isKindOfClass:NSArray.class]) {
|
||||
NSLog(@"[KBSuggestionEngine] Invalid words array in indonesian_words.json");
|
||||
return [self.class kb_defaultWords];
|
||||
}
|
||||
|
||||
NSMutableArray<NSString *> *result = [NSMutableArray array];
|
||||
for (id item in wordsArray) {
|
||||
if ([item isKindOfClass:NSString.class]) {
|
||||
[result addObject:item];
|
||||
}
|
||||
}
|
||||
|
||||
NSLog(@"[KBSuggestionEngine] Loaded %lu Indonesian words", (unsigned long)result.count);
|
||||
return result.count > 0 ? [result copy] : [self.class kb_defaultWords];
|
||||
}
|
||||
|
||||
#pragma mark - Word List Helpers
|
||||
|
||||
- (NSArray<NSString *> *)kb_suggestionsFromWordList:(NSArray<NSString *> *)words
|
||||
prefix:(NSString *)prefix
|
||||
limit:(NSUInteger)limit {
|
||||
NSString *lower = prefix.lowercaseString;
|
||||
NSMutableArray<NSString *> *matches = [NSMutableArray array];
|
||||
|
||||
for (NSString *word in words) {
|
||||
if ([word hasPrefix:lower]) {
|
||||
[matches addObject:word];
|
||||
if (matches.count >= limit * 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (matches.count == 0) { return @[]; }
|
||||
|
||||
[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;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user