diff --git a/Source/Modules/ControllerModules/SyllableComposer.swift b/Source/Modules/ControllerModules/SyllableComposer.swift index b34d0982..5c76dca9 100644 --- a/Source/Modules/ControllerModules/SyllableComposer.swift +++ b/Source/Modules/ControllerModules/SyllableComposer.swift @@ -48,6 +48,7 @@ public struct Tekkon { case ofMiTAC = 6 case ofSeigyou = 7 case ofFakeSeigyou = 8 + case ofStarlight = 9 case ofHanyuPinyin = 100 case ofSecondaryPinyin = 101 case ofYalePinyin = 102 @@ -74,6 +75,8 @@ public struct Tekkon { return "FakeSeigyou" case .ofSeigyou: return "Seigyou" + case .ofStarlight: + return "Starlight" case .ofHanyuPinyin: return "HanyuPinyin" case .ofSecondaryPinyin: @@ -334,6 +337,8 @@ public struct Tekkon { return Tekkon.mapSeigyou[input] != nil case .ofFakeSeigyou: return Tekkon.mapFakeSeigyou[input] != nil + case .ofStarlight: + return Tekkon.mapStarlightStaticKeys[input] != nil case .ofHanyuPinyin, .ofSecondaryPinyin, .ofYalePinyin, .ofHualuoPinyin, .ofUniversalPinyin: return Tekkon.mapArayuruPinyin.contains(input) } @@ -390,7 +395,10 @@ public struct Tekkon { case "ㄧ", "ㄩ": if vowel.value == "ㄜ" { vowel = "ㄝ" } case "ㄜ": + if "ㄨ".contains(semivowel.value) { semivowel = "ㄩ" } if "ㄧㄩ".contains(semivowel.value) { thePhone = "ㄝ" } + case "ㄝ": + if "ㄨ".contains(semivowel.value) { semivowel = "ㄩ" } case "ㄛ", "ㄥ": if "ㄅㄆㄇㄈ".contains(consonant.value), semivowel.value == "ㄨ" { semivowel.clear() } case "ㄟ": @@ -398,6 +406,8 @@ public struct Tekkon { case "ㄨ": if "ㄅㄆㄇㄈ".contains(consonant.value), "ㄛㄥ".contains(vowel.value) { vowel.clear() } if "ㄋㄌ".contains(consonant.value), "ㄟ".contains(vowel.value) { vowel.clear() } + if "ㄜ".contains(vowel.value) { vowel = "ㄝ" } + if "ㄝ".contains(vowel.value) { thePhone = "ㄩ" } case "ㄅ", "ㄆ", "ㄇ", "ㄈ": if ["ㄨㄛ", "ㄨㄥ"].contains(semivowel.value + vowel.value) { semivowel.clear() } default: break @@ -537,12 +547,61 @@ public struct Tekkon { return Tekkon.mapSeigyou[key] ?? "" case .ofFakeSeigyou: return Tekkon.mapFakeSeigyou[key] ?? "" + case .ofStarlight: + return handleStarlight(key: key) case .ofHanyuPinyin, .ofSecondaryPinyin, .ofYalePinyin, .ofHualuoPinyin, .ofUniversalPinyin: break // 漢語拼音單獨用另外的函式處理 } return "" } + /// 所有動態注音鍵盤佈局都會用到的共用糾錯處理步驟。 + /// - Parameter incomingPhonabet: 傳入的注音 Phonabet。 + mutating func commonFixWhenHandlingDynamicArrangeInputs(target incomingPhonabet: Phonabet) { + // 處理特殊情形。 + switch incomingPhonabet.type { + case .semivowel: + switch consonant { + case "ㄍ": + switch incomingPhonabet { + case "ㄧ": consonant = "ㄑ" // ㄑㄧ + case "ㄨ": consonant = "ㄍ" // ㄍㄨ + case "ㄩ": consonant = "ㄑ" // ㄑㄩ + default: break + } + case "ㄓ": + switch incomingPhonabet { + case "ㄧ": consonant = "ㄐ" // ㄐㄧ + case "ㄨ": consonant = "ㄓ" // ㄓㄨ + case "ㄩ": consonant = "ㄐ" // ㄐㄩ + default: break + } + case "ㄔ": + switch incomingPhonabet { + case "ㄧ": consonant = "ㄑ" // ㄐㄧ + case "ㄨ": consonant = "ㄔ" // ㄓㄨ + case "ㄩ": consonant = "ㄑ" // ㄐㄩ + default: break + } + case "ㄕ": + switch incomingPhonabet { + case "ㄧ": consonant = "ㄒ" // ㄒㄧ + case "ㄨ": consonant = "ㄕ" // ㄕㄨ + case "ㄩ": consonant = "ㄒ" // ㄒㄩ + default: break + } + default: break + } + case .vowel: + if semivowel.isEmpty { + consonant.selfReplace("ㄐ", "ㄓ") + consonant.selfReplace("ㄑ", "ㄔ") + consonant.selfReplace("ㄒ", "ㄕ") + } + default: break + } + } + /// 倚天忘形注音排列比較麻煩,需要單獨處理。 /// /// 回傳結果是空字串的話,不要緊,因為該函式內部已經處理過分配過程了。 @@ -577,33 +636,8 @@ public struct Tekkon { default: break } - // 處理「一個按鍵對應兩個聲母」的情形。 - if !consonant.isEmpty, incomingPhonabet.type == .semivowel { - switch consonant { - case "ㄍ": - switch incomingPhonabet { - case "ㄧ": consonant = "ㄑ" // ㄑㄧ - case "ㄨ": consonant = "ㄍ" // ㄍㄨ - case "ㄩ": consonant = "ㄑ" // ㄑㄩ - default: break - } - case "ㄓ": - switch incomingPhonabet { - case "ㄧ": consonant = "ㄐ" // ㄐㄧ - case "ㄨ": consonant = "ㄓ" // ㄓㄨ - case "ㄩ": consonant = "ㄐ" // ㄐㄩ - default: break - } - case "ㄕ": - switch incomingPhonabet { - case "ㄧ": consonant = "ㄒ" // ㄒㄧ - case "ㄨ": consonant = "ㄕ" // ㄕㄨ - case "ㄩ": consonant = "ㄒ" // ㄒㄩ - default: break - } - default: break - } - } + // 處理特殊情形。 + commonFixWhenHandlingDynamicArrangeInputs(target: incomingPhonabet) if "dfjk ".contains(key), !consonant.isEmpty, semivowel.isEmpty, vowel.isEmpty @@ -666,51 +700,7 @@ public struct Tekkon { } // 處理特殊情形。 - switch incomingPhonabet.type { - case .semivowel: - switch consonant { - case "ㄍ": // 許氏鍵盤應該也需要這個自動糾正 - switch incomingPhonabet { - case "ㄧ": consonant = "ㄑ" // ㄑㄧ - case "ㄨ": consonant = "ㄍ" // ㄍㄨ - case "ㄩ": consonant = "ㄑ" // ㄑㄩ - default: break - } - case "ㄓ": - if intonation.isEmpty { - switch incomingPhonabet { - case "ㄧ": consonant = "ㄐ" // ㄐㄧ - case "ㄨ": consonant = "ㄓ" // ㄓㄨ - case "ㄩ": consonant = "ㄐ" // ㄐㄩ - default: break - } - } - case "ㄔ": - if intonation.isEmpty { - switch incomingPhonabet { - case "ㄧ": consonant = "ㄑ" // ㄐㄧ - case "ㄨ": consonant = "ㄔ" // ㄓㄨ - case "ㄩ": consonant = "ㄑ" // ㄐㄩ - default: break - } - } - case "ㄕ": - switch incomingPhonabet { - case "ㄧ": consonant = "ㄒ" // ㄒㄧ - case "ㄨ": consonant = "ㄕ" // ㄕㄨ - case "ㄩ": consonant = "ㄒ" // ㄒㄩ - default: break - } - default: break - } - case .vowel: - if semivowel.isEmpty { - consonant.selfReplace("ㄐ", "ㄓ") - consonant.selfReplace("ㄑ", "ㄔ") - consonant.selfReplace("ㄒ", "ㄕ") - } - default: break - } + commonFixWhenHandlingDynamicArrangeInputs(target: incomingPhonabet) if "dfjs ".contains(key) { if !consonant.isEmpty, semivowel.isEmpty, vowel.isEmpty { @@ -749,6 +739,48 @@ public struct Tekkon { return strReturn } + /// 星光排列一樣同樣也比較麻煩,需要單獨處理。 + /// + /// 回傳結果是空的話,不要緊,因為該函式內部已經處理過分配過程了。 + /// - Parameters: + /// - key: 傳入的 String 訊號。 + mutating func handleStarlight(key: String) -> String { + var strReturn = Tekkon.mapStarlightStaticKeys[key] ?? "" + let incomingPhonabet = Phonabet(strReturn) + switch key { + case "e": return "ㄧㄩ".contains(semivowel.value) ? "ㄝ" : "ㄜ" + case "f": return vowel == "ㄠ" || !isPronouncable ? "ㄈ" : "ㄠ" + case "g": return vowel == "ㄥ" || !isPronouncable ? "ㄍ" : "ㄥ" + case "k": return vowel == "ㄤ" || !isPronouncable ? "ㄎ" : "ㄤ" + case "l": return vowel == "ㄦ" || !isPronouncable ? "ㄌ" : "ㄦ" + case "m": return vowel == "ㄢ" || !isPronouncable ? "ㄇ" : "ㄢ" + case "n": return vowel == "ㄣ" || !isPronouncable ? "ㄋ" : "ㄣ" + case "t": return vowel == "ㄟ" || !isPronouncable ? "ㄊ" : "ㄟ" + default: break + } + + // 處理特殊情形。 + commonFixWhenHandlingDynamicArrangeInputs(target: incomingPhonabet) + + if "67890 ".contains(key) { + if !consonant.isEmpty, semivowel.isEmpty, vowel.isEmpty { + consonant.selfReplace("ㄈ", "ㄠ") + consonant.selfReplace("ㄍ", "ㄥ") + consonant.selfReplace("ㄎ", "ㄤ") + consonant.selfReplace("ㄌ", "ㄦ") + consonant.selfReplace("ㄇ", "ㄢ") + consonant.selfReplace("ㄋ", "ㄣ") + consonant.selfReplace("ㄊ", "ㄟ") + } + } + + // 這些按鍵在上文處理過了,就不要再回傳了。 + if "efgklmn".contains(key) { strReturn = "" } + + // 回傳結果是空的話,不要緊,因為上文已經代處理過分配過程了。 + return strReturn + } + /// 大千忘形一樣同樣也比較麻煩,需要單獨處理。 /// /// 回傳結果是空的話,不要緊,因為該函式內部已經處理過分配過程了。 @@ -765,7 +797,13 @@ public struct Tekkon { case "b": if !consonant.isEmpty || !semivowel.isEmpty { vowel = "ㄝ" } else { consonant = "ㄖ" } case "i": if vowel.isEmpty || vowel == "ㄞ" { vowel = "ㄛ" } else { vowel = "ㄞ" } case "l": if vowel.isEmpty || vowel == "ㄤ" { vowel = "ㄠ" } else { vowel = "ㄤ" } - case "n": if !consonant.isEmpty || !semivowel.isEmpty { vowel = "ㄥ" } else { consonant = "ㄙ" } + case "n": + if !consonant.isEmpty || !semivowel.isEmpty { + if consonant == "ㄙ", semivowel.isEmpty, vowel.isEmpty { consonant.clear() } + vowel = "ㄥ" + } else { + consonant = "ㄙ" + } case "o": if vowel.isEmpty || vowel == "ㄢ" { vowel = "ㄟ" } else { vowel = "ㄢ" } case "p": if vowel.isEmpty || vowel == "ㄦ" { vowel = "ㄣ" } else { vowel = "ㄦ" } case "q": if consonant.isEmpty || consonant == "ㄅ" { consonant = "ㄆ" } else { consonant = "ㄅ" } @@ -781,7 +819,7 @@ public struct Tekkon { } else if !semivowel.isEmpty { vowel = "ㄡ" } else { - semivowel = "ㄩ" + receiveKey(fromPhonabet: "ㄐㄑㄒ".contains(consonant.value) ? "ㄩ" : "ㄡ") } case "u": if semivowel == "ㄧ", vowel != "ㄚ" { @@ -895,9 +933,11 @@ public struct Tekkon { } /// 該函式用來將漢語拼音轉為注音。 - /// - Parameter target: 要轉換的漢語拼音內容,要求必須帶有 12345 數字標調。 + /// - Parameters: + /// - target: 要轉換的漢語拼音內容,要求必須帶有 12345 數字標調。 + /// - newToneOne: 對陰平指定新的標記。預設情況下該標記為空字串。 /// - Returns: 轉換結果。 - static func cnvHanyuPinyinToPhona(target: String) -> String { + static func cnvHanyuPinyinToPhona(target: String, newToneOne: String = "") -> String { if target.contains("_") { return target } var result = target for key in Tekkon.mapHanyuPinyin.keys.sorted(by: { $0.count > $1.count }) { @@ -906,7 +946,7 @@ public struct Tekkon { } for key in Tekkon.mapArayuruPinyinIntonation.keys.sorted(by: { $0.count > $1.count }) { guard let value = Tekkon.mapArayuruPinyinIntonation[key] else { continue } - result = result.replacingOccurrences(of: key, with: (key == "1") ? "" : value) + result = result.replacingOccurrences(of: key, with: (key == "1") ? newToneOne : value) } return result } @@ -1371,6 +1411,17 @@ public struct Tekkon { "w": "ㄘ", "x": "ㄨ", "y": "ㄔ", "z": "ㄠ", " ": " ", ] + /// 星光排列預處理專用陣列,但未包含全部的映射內容。 + /// + /// 在這裡將二十六個字母寫全,也只是為了方便做 validity check。 + /// 這裡提前對複音按鍵做處理,然後再用程式判斷介母類型、據此判斷是否需要做複音切換。 + static let mapStarlightStaticKeys: [String: String] = [ + "a": "ㄚ", "b": "ㄅ", "c": "ㄘ", "d": "ㄉ", "e": "ㄜ", "f": "ㄈ", "g": "ㄍ", "h": "ㄏ", "i": "ㄧ", "j": "ㄓ", "k": "ㄎ", + "l": "ㄌ", "m": "ㄇ", "n": "ㄋ", "o": "ㄛ", "p": "ㄆ", "q": "ㄔ", "r": "ㄖ", "s": "ㄙ", "t": "ㄊ", "u": "ㄨ", "v": "ㄩ", + "w": "ㄡ", "x": "ㄕ", "y": "ㄞ", "z": "ㄗ", " ": " ", "1": " ", "2": "ˊ", "3": "ˇ", "4": "ˋ", "5": "˙", "6": " ", + "7": "ˊ", "8": "ˇ", "9": "ˋ", "0": "˙", + ] + /// 倚天傳統排列專用處理陣列。 static let mapQwertyETenTraditional: [String: String] = [ "'": "ㄘ", ",": "ㄓ", "-": "ㄥ", ".": "ㄔ", "/": "ㄕ", "0": "ㄤ", "1": "˙", "2": "ˊ", "3": "ˇ", "4": "ˋ", "7": "ㄑ",