From 04cfc354857c6afd5dc1ff139da1096bd0049691 Mon Sep 17 00:00:00 2001 From: CodeST <694468528@qq.com> Date: Mon, 2 Mar 2026 16:34:59 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=A5=BF=E7=8F=AD=E7=89=99?= =?UTF-8?q?=E8=AF=8D=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CustomKeyboard/Manager/KBSuggestionEngine.h | 3 +- CustomKeyboard/Manager/KBSuggestionEngine.m | 78 ++++++++++++++++++- CustomKeyboard/Resource/spanish_words.json | 85 +++++++++++++++++++++ Shared/Resource/kb_input_profiles.json | 4 +- keyBoard.xcodeproj/project.pbxproj | 4 + 5 files changed, 170 insertions(+), 4 deletions(-) create mode 100644 CustomKeyboard/Resource/spanish_words.json diff --git a/CustomKeyboard/Manager/KBSuggestionEngine.h b/CustomKeyboard/Manager/KBSuggestionEngine.h index 02aee96..679a238 100644 --- a/CustomKeyboard/Manager/KBSuggestionEngine.h +++ b/CustomKeyboard/Manager/KBSuggestionEngine.h @@ -8,7 +8,8 @@ NS_ASSUME_NONNULL_BEGIN typedef NS_ENUM(NSInteger, KBSuggestionEngineType) { - KBSuggestionEngineTypeLatin = 0, // 拉丁字母(英语、西班牙语、葡萄牙语、印尼语) + KBSuggestionEngineTypeLatin = 0, // 拉丁字母(英语、葡萄牙语、印尼语) + KBSuggestionEngineTypeSpanish, // 西班牙语 KBSuggestionEngineTypePinyinSimplified, // 简体拼音 KBSuggestionEngineTypePinyinTraditional, // 繁体拼音 KBSuggestionEngineTypeBopomofo // 注音(繁体) diff --git a/CustomKeyboard/Manager/KBSuggestionEngine.m b/CustomKeyboard/Manager/KBSuggestionEngine.m index 142b8b0..a3bbd8c 100644 --- a/CustomKeyboard/Manager/KBSuggestionEngine.m +++ b/CustomKeyboard/Manager/KBSuggestionEngine.m @@ -14,6 +14,7 @@ @property (nonatomic, copy) NSArray *simplifiedChineseWords; @property (nonatomic, strong) NSDictionary *> *pinyinToTraditionalMap; @property (nonatomic, strong) NSDictionary *> *bopomofoToChineseMap; +@property (nonatomic, copy) NSArray *spanishWords; @end @implementation KBSuggestionEngine @@ -38,6 +39,7 @@ _simplifiedChineseWords = [self kb_loadSimplifiedChineseWords]; _pinyinToTraditionalMap = [self kb_loadPinyinToTraditionalMap]; _bopomofoToChineseMap = [self kb_loadBopomofoToChineseMap]; + _spanishWords = [self kb_loadSpanishWords]; } return self; } @@ -45,8 +47,9 @@ - (NSArray *)suggestionsForPrefix:(NSString *)prefix limit:(NSUInteger)limit { if (prefix.length == 0 || limit == 0) { return @[]; } - // 根据引擎类型选择不同的联想逻辑 switch (self.engineType) { + case KBSuggestionEngineTypeSpanish: + return [self kb_spanishSuggestionsForPrefix:prefix limit:limit]; case KBSuggestionEngineTypePinyinTraditional: return [self kb_traditionalPinyinSuggestionsForPrefix:prefix limit:limit]; case KBSuggestionEngineTypePinyinSimplified: @@ -158,6 +161,8 @@ - (void)setEngineTypeFromString:(NSString *)engineTypeString { if ([engineTypeString isEqualToString:@"latin"]) { self.engineType = KBSuggestionEngineTypeLatin; + } else if ([engineTypeString isEqualToString:@"spanish"]) { + self.engineType = KBSuggestionEngineTypeSpanish; } else if ([engineTypeString isEqualToString:@"pinyin_traditional"]) { self.engineType = KBSuggestionEngineTypePinyinTraditional; } else if ([engineTypeString isEqualToString:@"pinyin_simplified"]) { @@ -531,4 +536,75 @@ return [result copy]; } +#pragma mark - Spanish Suggestions + +- (NSArray *)kb_spanishSuggestionsForPrefix:(NSString *)prefix limit:(NSUInteger)limit { + NSString *lower = prefix.lowercaseString; + NSMutableArray *matches = [NSMutableArray array]; + + for (NSString *word in self.spanishWords) { + if ([word hasPrefix:lower]) { + [matches addObject:word]; + if (matches.count >= limit * 2) { + break; + } + } + } + + 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; +} + +- (NSArray *)kb_loadSpanishWords { + NSString *path = [[NSBundle mainBundle] pathForResource:@"spanish_words" ofType:@"json"]; + if (!path) { + NSLog(@"[KBSuggestionEngine] spanish_words.json not found, using default words"); + return [self.class kb_defaultWords]; + } + + NSData *data = [NSData dataWithContentsOfFile:path]; + if (!data) { + NSLog(@"[KBSuggestionEngine] Failed to read spanish_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 spanish_words.json: %@", error); + return [self.class kb_defaultWords]; + } + + NSArray *wordsArray = json[@"words"]; + if (![wordsArray isKindOfClass:NSArray.class]) { + NSLog(@"[KBSuggestionEngine] Invalid words array in spanish_words.json"); + return [self.class kb_defaultWords]; + } + + NSMutableArray *result = [NSMutableArray array]; + for (id item in wordsArray) { + if ([item isKindOfClass:NSString.class]) { + [result addObject:item]; + } + } + + NSLog(@"[KBSuggestionEngine] Loaded %lu Spanish words", (unsigned long)result.count); + return result.count > 0 ? [result copy] : [self.class kb_defaultWords]; +} + @end diff --git a/CustomKeyboard/Resource/spanish_words.json b/CustomKeyboard/Resource/spanish_words.json new file mode 100644 index 0000000..4d10a55 --- /dev/null +++ b/CustomKeyboard/Resource/spanish_words.json @@ -0,0 +1,85 @@ +{ + "__comment": "西班牙语(拉丁美洲)常用词库", + "words": [ + "hola", "gracias", "por favor", "buenos días", "buenas tardes", + "buenas noches", "adiós", "hasta luego", "cómo estás", "muy bien", + "qué", "dónde", "cuándo", "por qué", "quién", + "cómo", "cuánto", "cuál", "cuáles", "dónde", + "aquí", "allí", "ahora", "después", "antes", + "hoy", "mañana", "ayer", "siempre", "nunca", + "también", "tampoco", "sí", "no", "tal vez", + "yo", "tú", "él", "ella", "nosotros", + "ellos", "ellas", "usted", "ustedes", "mí", + "ti", "sí", "conmigo", "contigo", "consigo", + "ser", "estar", "tener", "haber", "hacer", + "ir", "venir", "decir", "ver", "poder", + "dar", "saber", "querer", "pensar", "sentir", + "creer", "vivir", "morir", "nacer", "crecer", + "hablar", "escuchar", "mirar", "tocar", "oler", + "gustar", "amar", "odiar", "esperar", "temer", + "casa", "coche", "trabajo", "dinero", "tiempo", + "año", "mes", "semana", "día", "hora", + "minuto", "segundo", "momento", "vida", "muerte", + "amor", "odio", "paz", "guerra", "libertad", + "amigo", "amiga", "hermano", "hermana", "padre", + "madre", "hijo", "hija", "abuelo", "abuela", + "tío", "tía", "primo", "prima", "esposo", + "esposa", "novio", "novia", "familia", "pareja", + "agua", "comida", "ropa", "cama", "mesa", + "silla", "puerta", "ventana", "pared", "suelo", + "cielo", "sol", "luna", "estrella", "nube", + "lluvia", "nieve", "viento", "fuego", "tierra", + "árbol", "flor", "fruta", "verdura", "carne", + "pescado", "pan", "leche", "café", "cerveza", + "vino", "jugo", "azúcar", "sal", "aceite", + "grande", "pequeño", "alto", "bajo", "largo", + "corto", "ancho", "estrecho", "gordo", "delgado", + "bonito", "feo", "bueno", "malo", "nuevo", + "viejo", "joven", "antiguo", "moderno", "fácil", + "difícil", "rápido", "lento", "fuerte", "débil", + "caliente", "frío", "tibio", "seco", "mojado", + "limpio", "sucio", "ordenado", "desordenado", "lleno", + "vacío", "abierto", "cerrado", "encendido", "apagado", + "uno", "dos", "tres", "cuatro", "cinco", + "seis", "siete", "ocho", "nueve", "diez", + "once", "doce", "trece", "catorce", "quince", + "veinte", "treinta", "cuarenta", "cincuenta", "cien", + "mil", "millón", "primero", "segundo", "tercero", + "último", "siguiente", "anterior", "nuevo", "otro", + "mismo", "diferente", "igual", "similar", "opuesto", + "derecha", "izquierda", "arriba", "abajo", "delante", + "detrás", "dentro", "fuera", "cerca", "lejos", + "encima", "debajo", "entre", "alrededor", "a través", + "hacia", "desde", "hasta", "para", "por", + "con", "sin", "sobre", "bajo", "entre", + "contra", "según", "durante", "mediante", "excepto", + "salvo", "incluso", "aun", "aunque", "pero", + "sino", "porque", "pues", "como", "si", + "cuando", "mientras", "aunque", "a pesar de", "sin embargo", + "además", "también", "asimismo", "por lo tanto", "por consiguiente", + "así que", "de modo que", "ya que", "dado que", "visto que", + "es necesario", "es importante", "es posible", "es probable", "es evidente", + "me gusta", "me encanta", "me interesa", "me importa", "me parece", + "creo que", "pienso que", "digo que", "sé que", "veo que", + "quiero", "puedo", "debo", "tengo que", "hay que", + "vamos a", "vengo de", "voy a", "tengo que", "hay que", + "está bien", "está mal", "no importa", "no problem", "claro", + "claro que sí", "claro que no", "por supuesto", "exacto", "correcto", + "perfecto", "excelente", "genial", "increíble", "impresionante", + "maravilloso", "fantástico", "terrible", "horrible", "espantoso", + "increíble", "asombroso", "sorprendente", "extraño", "raro", + "normal", "común", "frecuente", "habitual", "usual", + "especial", "único", "particular", "específico", "concreto", + "general", "total", "completo", "absoluto", "relativo", + "verdadero", "falso", "real", "imaginario", "posible", + "imposible", "probable", "improbable", "seguro", "incierto", + "cierto", "dudoso", "claro", "oscuro", "confuso", + "simple", "complicado", "complejo", "sencillo", "fácil", + "dificil", "imposible", "posible", "probable", "improbable", + "mexico", "españa", "argentina", "colombia", "perú", + "chile", "venezuela", "ecuador", "guatemala", "cuba", + "bolivia", "honduras", "paraguay", "el salvador", "nicaragua", + "costa rica", "panamá", "uruguay", "puerto rico", "república dominicana", + "latinoamérica", "hispanoamérica", "américa latina", "el mundo", "el país" + ] +} diff --git a/Shared/Resource/kb_input_profiles.json b/Shared/Resource/kb_input_profiles.json index 76bc884..a5f4b66 100644 --- a/Shared/Resource/kb_input_profiles.json +++ b/Shared/Resource/kb_input_profiles.json @@ -18,14 +18,14 @@ { "code": "es", "name": "Español (Latinoamérica)", - "defaultSkinZip": "西班牙初始皮肤.zip", + "defaultSkinZip": "", "layouts": [ { "variant": "qwerty", "title": "QWERTY", "profileId": "es_419_qwerty", "layoutJsonId": "letters_es", - "suggestionEngine": "latin" + "suggestionEngine": "spanish" } ] }, diff --git a/keyBoard.xcodeproj/project.pbxproj b/keyBoard.xcodeproj/project.pbxproj index 5bbd601..62300c2 100644 --- a/keyBoard.xcodeproj/project.pbxproj +++ b/keyBoard.xcodeproj/project.pbxproj @@ -45,6 +45,7 @@ 043213B12F556DF80065C888 /* KBSkinIconMap_es.strings in Resources */ = {isa = PBXBuildFile; fileRef = 043213AB2F556DF80065C888 /* KBSkinIconMap_es.strings */; }; 043213B22F556DF80065C888 /* KBSkinIconMap_zh_hant.strings in Resources */ = {isa = PBXBuildFile; fileRef = 043213AE2F556DF80065C888 /* KBSkinIconMap_zh_hant.strings */; }; 043213B42F557CC50065C888 /* 西班牙初始皮肤.zip in Resources */ = {isa = PBXBuildFile; fileRef = 043213B32F557CC50065C888 /* 西班牙初始皮肤.zip */; }; + 043213B62F5582710065C888 /* spanish_words.json in Resources */ = {isa = PBXBuildFile; fileRef = 043213B52F5582710065C888 /* spanish_words.json */; }; 043FBCD22EAF97630036AFE1 /* KBPermissionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 04C6EAE12EAF940F0089C901 /* KBPermissionViewController.m */; }; 0450AA742EF013D000B6AF06 /* KBEmojiCollectionCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 0450AA732EF013D000B6AF06 /* KBEmojiCollectionCell.m */; }; 0450AAE22EF03D5100B6AF06 /* KBPerson.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0450AAE12EF03D5100B6AF06 /* KBPerson.swift */; }; @@ -402,6 +403,7 @@ 043213AD2F556DF80065C888 /* KBSkinIconMap_pt.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = KBSkinIconMap_pt.strings; sourceTree = ""; }; 043213AE2F556DF80065C888 /* KBSkinIconMap_zh_hant.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = KBSkinIconMap_zh_hant.strings; sourceTree = ""; }; 043213B32F557CC50065C888 /* 西班牙初始皮肤.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = "西班牙初始皮肤.zip"; sourceTree = ""; }; + 043213B52F5582710065C888 /* spanish_words.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = spanish_words.json; sourceTree = ""; }; 0450AA722EF013D000B6AF06 /* KBEmojiCollectionCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBEmojiCollectionCell.h; sourceTree = ""; }; 0450AA732EF013D000B6AF06 /* KBEmojiCollectionCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBEmojiCollectionCell.m; sourceTree = ""; }; 0450AAE02EF03D5100B6AF06 /* keyBoard-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "keyBoard-Bridging-Header.h"; sourceTree = ""; }; @@ -945,6 +947,7 @@ 043213AC2F556DF80065C888 /* KBSkinIconMap_id.strings */, 043213AD2F556DF80065C888 /* KBSkinIconMap_pt.strings */, 043213AE2F556DF80065C888 /* KBSkinIconMap_zh_hant.strings */, + 043213B52F5582710065C888 /* spanish_words.json */, ); path = Resource; sourceTree = ""; @@ -2311,6 +2314,7 @@ 043213A42F5528140065C888 /* pinyin_to_traditional.json in Resources */, 043213AA2F5566EF0065C888 /* kb_input_profiles.json in Resources */, 04286A0B2ECD88B400CE730C /* KeyboardAssets.xcassets in Resources */, + 043213B62F5582710065C888 /* spanish_words.json in Resources */, 043213A62F5561FD0065C888 /* kb_keyboard_layouts_i18n.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0;