BrailleSputnik // Initial Implementation.
This commit is contained in:
parent
549c361af4
commit
7be2a85b25
|
@ -0,0 +1,8 @@
|
||||||
|
.DS_Store
|
||||||
|
/.build
|
||||||
|
/Packages
|
||||||
|
xcuserdata/
|
||||||
|
DerivedData/
|
||||||
|
.swiftpm/configuration/registries.json
|
||||||
|
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||||
|
.netrc
|
|
@ -0,0 +1,35 @@
|
||||||
|
// swift-tools-version: 5.7
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "BrailleSputnik",
|
||||||
|
platforms: [
|
||||||
|
.macOS(.v11),
|
||||||
|
],
|
||||||
|
products: [
|
||||||
|
// Products define the executables and libraries a package produces, making them visible to other packages.
|
||||||
|
.library(
|
||||||
|
name: "BrailleSputnik",
|
||||||
|
targets: ["BrailleSputnik"]
|
||||||
|
),
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
.package(path: "../vChewing_Shared"),
|
||||||
|
.package(path: "../vChewing_Tekkon"),
|
||||||
|
],
|
||||||
|
targets: [
|
||||||
|
// Targets are the basic building blocks of a package, defining a module or a test suite.
|
||||||
|
// Targets can depend on other targets in this package and products from dependencies.
|
||||||
|
.target(
|
||||||
|
name: "BrailleSputnik",
|
||||||
|
dependencies: [
|
||||||
|
.product(name: "Shared", package: "vChewing_Shared"),
|
||||||
|
.product(name: "Tekkon", package: "vChewing_Tekkon"),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
.testTarget(
|
||||||
|
name: "BrailleSputnikTests",
|
||||||
|
dependencies: ["BrailleSputnik"]
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
|
@ -0,0 +1,284 @@
|
||||||
|
// (c) 2022 and onwards The vChewing Project (MIT-NTL License).
|
||||||
|
// ====================
|
||||||
|
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
|
||||||
|
// ... with NTL restriction stating that:
|
||||||
|
// No trademark license is granted to use the trade names, trademarks, service
|
||||||
|
// marks, or product names of Contributor, except as required to fulfill notice
|
||||||
|
// requirements defined in MIT License.
|
||||||
|
|
||||||
|
extension BrailleSputnik {
|
||||||
|
enum Braille: String {
|
||||||
|
case blank = "⠀" // U+2800
|
||||||
|
case d1 = "⠁"
|
||||||
|
case d2 = "⠂"
|
||||||
|
case d12 = "⠃"
|
||||||
|
case d3 = "⠄"
|
||||||
|
case d13 = "⠅"
|
||||||
|
case d23 = "⠆"
|
||||||
|
case d123 = "⠇"
|
||||||
|
case d4 = "⠈"
|
||||||
|
case d14 = "⠉"
|
||||||
|
case d24 = "⠊"
|
||||||
|
case d124 = "⠋"
|
||||||
|
case d34 = "⠌"
|
||||||
|
case d134 = "⠍"
|
||||||
|
case d234 = "⠎"
|
||||||
|
case d1234 = "⠏"
|
||||||
|
case d5 = "⠐"
|
||||||
|
case d15 = "⠑"
|
||||||
|
case d25 = "⠒"
|
||||||
|
case d125 = "⠓"
|
||||||
|
case d35 = "⠔"
|
||||||
|
case d135 = "⠕"
|
||||||
|
case d235 = "⠖"
|
||||||
|
case d1235 = "⠗"
|
||||||
|
case d45 = "⠘"
|
||||||
|
case d145 = "⠙"
|
||||||
|
case d245 = "⠚"
|
||||||
|
case d1245 = "⠛"
|
||||||
|
case d345 = "⠜"
|
||||||
|
case d1345 = "⠝"
|
||||||
|
case d2345 = "⠞"
|
||||||
|
case d12345 = "⠟"
|
||||||
|
case d6 = "⠠"
|
||||||
|
case d16 = "⠡"
|
||||||
|
case d26 = "⠢"
|
||||||
|
case d126 = "⠣"
|
||||||
|
case d36 = "⠤"
|
||||||
|
case d136 = "⠥"
|
||||||
|
case d236 = "⠦"
|
||||||
|
case d1236 = "⠧"
|
||||||
|
case d46 = "⠨"
|
||||||
|
case d146 = "⠩"
|
||||||
|
case d246 = "⠪"
|
||||||
|
case d1246 = "⠫"
|
||||||
|
case d346 = "⠬"
|
||||||
|
case d1346 = "⠭"
|
||||||
|
case d2346 = "⠮"
|
||||||
|
case d12346 = "⠯"
|
||||||
|
case d56 = "⠰"
|
||||||
|
case d156 = "⠱"
|
||||||
|
case d256 = "⠲"
|
||||||
|
case d1256 = "⠳"
|
||||||
|
case d356 = "⠴"
|
||||||
|
case d1356 = "⠵"
|
||||||
|
case d2356 = "⠶"
|
||||||
|
case d12356 = "⠷"
|
||||||
|
case d456 = "⠸"
|
||||||
|
case d1456 = "⠹"
|
||||||
|
case d2456 = "⠺"
|
||||||
|
case d12456 = "⠻"
|
||||||
|
case d3456 = "⠼"
|
||||||
|
case d13456 = "⠽"
|
||||||
|
case d23456 = "⠾"
|
||||||
|
case d123456 = "⠿"
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum BrailleStandard: Int {
|
||||||
|
case of1947 = 1
|
||||||
|
case of2018 = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol BrailleProcessingUnit {
|
||||||
|
var mapConsonants: [String: String] { get }
|
||||||
|
var mapSemivowels: [String: String] { get }
|
||||||
|
var mapVowels: [String: String] { get }
|
||||||
|
var mapIntonations: [String: String] { get }
|
||||||
|
var mapIntonationSpecialCases: [String: String] { get }
|
||||||
|
var mapCombinedVowels: [String: String] { get }
|
||||||
|
var mapPunctuations: [String: String] { get }
|
||||||
|
|
||||||
|
func handleSpecialCases(target: inout String, value: String?) -> Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Static Data conforming to 1947 Standard.
|
||||||
|
|
||||||
|
extension BrailleSputnik {
|
||||||
|
class BrailleProcessingUnit1947: BrailleProcessingUnit {
|
||||||
|
func handleSpecialCases(target _: inout String, value _: String?) -> Bool {
|
||||||
|
// 國語點字標準無最終例外處理步驟。
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
let mapConsonants: [String: String] = [
|
||||||
|
"ㄎ": "⠇", "ㄋ": "⠝", "ㄕ": "⠊",
|
||||||
|
"ㄌ": "⠉", "ㄆ": "⠏", "ㄇ": "⠍",
|
||||||
|
"ㄓ": "⠁", "ㄏ": "⠗", "ㄖ": "⠛",
|
||||||
|
"ㄅ": "⠕", "ㄑ": "⠚", "ㄘ": "⠚",
|
||||||
|
"ㄗ": "⠓", "ㄙ": "⠑", "ㄐ": "⠅",
|
||||||
|
"ㄉ": "⠙", "ㄈ": "⠟", "ㄔ": "⠃",
|
||||||
|
"ㄒ": "⠑", "ㄊ": "⠋", "ㄍ": "⠅",
|
||||||
|
]
|
||||||
|
|
||||||
|
let mapSemivowels: [String: String] = [
|
||||||
|
"ㄧ": "⠡", "ㄩ": "⠳", "ㄨ": "⠌",
|
||||||
|
]
|
||||||
|
|
||||||
|
let mapVowels: [String: String] = [
|
||||||
|
"ㄤ": "⠭", "ㄛ": "⠣", "ㄠ": "⠩",
|
||||||
|
"ㄞ": "⠺", "ㄜ": "⠮", "ㄡ": "⠷",
|
||||||
|
"ㄟ": "⠴", "ㄣ": "⠥", "ㄥ": "⠵",
|
||||||
|
"ㄢ": "⠧", "ㄚ": "⠜", "ㄦ": "⠱",
|
||||||
|
]
|
||||||
|
|
||||||
|
let mapIntonations: [String: String] = [
|
||||||
|
"˙": "⠱⠁", "ˇ": "⠈", "ˊ": "⠂", " ": "⠄", "ˋ": "⠐",
|
||||||
|
]
|
||||||
|
|
||||||
|
let mapIntonationSpecialCases: [String: String] = [
|
||||||
|
"ㄜ˙": "⠮⠁", "ㄚ˙": "⠜⠁", "ㄛ˙": "⠣⠁", "ㄣ˙": "⠥⠁",
|
||||||
|
]
|
||||||
|
|
||||||
|
let mapCombinedVowels: [String: String] = [
|
||||||
|
"ㄧㄝ": "⠬", "ㄧㄣ": "⠹", "ㄩㄝ": "⠦",
|
||||||
|
"ㄨㄟ": "⠫", "ㄨㄥ": "⠯", "ㄨㄣ": "⠿",
|
||||||
|
"ㄨㄚ": "⠔", "ㄧㄡ": "⠎", "ㄧㄤ": "⠨",
|
||||||
|
"ㄧㄚ": "⠾", "ㄨㄛ": "⠒", "ㄧㄥ": "⠽",
|
||||||
|
"ㄨㄞ": "⠶", "ㄩㄥ": "⠖", "ㄧㄠ": "⠪",
|
||||||
|
"ㄧㄞ": "⠢", "ㄨㄤ": "⠸", "ㄩㄣ": "⠲",
|
||||||
|
"ㄧㄢ": "⠞", "ㄩㄢ": "⠘", "ㄨㄢ": "⠻",
|
||||||
|
]
|
||||||
|
|
||||||
|
let mapPunctuations: [String: String] = [
|
||||||
|
"。": "⠤⠀", "·": "⠤⠀", ",": "⠆", ";": "⠰",
|
||||||
|
"、": "⠠", "?": "⠕⠀", "!": "⠇⠀", ":": "⠒⠒",
|
||||||
|
"╴╴": "⠰⠰", "﹏﹏": "⠠⠤", "……": "⠐⠐⠐",
|
||||||
|
"—": "⠐⠂", "—— ——": "⠐⠂⠐⠂", "※": "⠈⠼", "◎": "⠪⠕",
|
||||||
|
"『": "⠦⠦", "』": "⠴⠴", "「": "⠰⠤", "」": "⠤⠆",
|
||||||
|
"‘": "⠦⠦", "’": "⠴⠴", "“": "⠰⠤", "”": "⠤⠆",
|
||||||
|
"(": "⠪", ")": "⠕", "〔": "⠯", "〕": "⠽",
|
||||||
|
"{": "⠦", "}": "⠴", "[": "⠯", "]": "⠽",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Static Data conforming to 2018 Standard (GF0019-2018)
|
||||||
|
|
||||||
|
extension BrailleSputnik {
|
||||||
|
class BrailleProcessingUnit2018: BrailleProcessingUnit {
|
||||||
|
func handleSpecialCases(target: inout String, value: String?) -> Bool {
|
||||||
|
guard let value = value else { return false }
|
||||||
|
switch value {
|
||||||
|
case "他": target = Braille.d2345.rawValue + Braille.d35.rawValue
|
||||||
|
case "它": target = Braille.d4.rawValue + Braille.d2345.rawValue + Braille.d35.rawValue
|
||||||
|
default: return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
let mapConsonants: [String: String] = [
|
||||||
|
"ㄅ": Braille.d12.rawValue,
|
||||||
|
"ㄆ": Braille.d1234.rawValue,
|
||||||
|
"ㄇ": Braille.d134.rawValue,
|
||||||
|
"ㄈ": Braille.d124.rawValue,
|
||||||
|
"ㄉ": Braille.d145.rawValue,
|
||||||
|
"ㄊ": Braille.d2345.rawValue,
|
||||||
|
"ㄋ": Braille.d1345.rawValue,
|
||||||
|
"ㄌ": Braille.d123.rawValue,
|
||||||
|
"ㄍ": Braille.d1245.rawValue,
|
||||||
|
"ㄎ": Braille.d13.rawValue,
|
||||||
|
"ㄏ": Braille.d125.rawValue,
|
||||||
|
"ㄐ": Braille.d1245.rawValue,
|
||||||
|
"ㄑ": Braille.d13.rawValue,
|
||||||
|
"ㄒ": Braille.d125.rawValue,
|
||||||
|
"ㄓ": Braille.d34.rawValue,
|
||||||
|
"ㄔ": Braille.d12345.rawValue,
|
||||||
|
"ㄕ": Braille.d156.rawValue,
|
||||||
|
"ㄖ": Braille.d245.rawValue,
|
||||||
|
"ㄗ": Braille.d1356.rawValue,
|
||||||
|
"ㄘ": Braille.d14.rawValue,
|
||||||
|
"ㄙ": Braille.d234.rawValue,
|
||||||
|
]
|
||||||
|
|
||||||
|
let mapSemivowels: [String: String] = [
|
||||||
|
"ㄧ": Braille.d24.rawValue,
|
||||||
|
"ㄨ": Braille.d136.rawValue,
|
||||||
|
"ㄩ": Braille.d346.rawValue,
|
||||||
|
]
|
||||||
|
|
||||||
|
let mapVowels: [String: String] = [
|
||||||
|
"ㄚ": Braille.d35.rawValue,
|
||||||
|
"ㄛ": Braille.d26.rawValue,
|
||||||
|
"ㄜ": Braille.d26.rawValue,
|
||||||
|
"ㄞ": Braille.d246.rawValue,
|
||||||
|
"ㄟ": Braille.d2346.rawValue,
|
||||||
|
"ㄠ": Braille.d235.rawValue,
|
||||||
|
"ㄡ": Braille.d12356.rawValue,
|
||||||
|
"ㄢ": Braille.d1236.rawValue,
|
||||||
|
"ㄣ": Braille.d356.rawValue,
|
||||||
|
"ㄤ": Braille.d236.rawValue,
|
||||||
|
"ㄥ": Braille.d3456.rawValue, // 該注音符號也有合併處理規則。
|
||||||
|
"ㄦ": Braille.d1235.rawValue,
|
||||||
|
]
|
||||||
|
|
||||||
|
let mapIntonations: [String: String] = [
|
||||||
|
" ": Braille.d1.rawValue,
|
||||||
|
"ˊ": Braille.d2.rawValue,
|
||||||
|
"ˇ": Braille.d3.rawValue,
|
||||||
|
"ˋ": Braille.d23.rawValue,
|
||||||
|
// "˙": nil, // 輕聲不設符號。
|
||||||
|
]
|
||||||
|
|
||||||
|
let mapIntonationSpecialCases: [String: String] = [:]
|
||||||
|
|
||||||
|
let mapCombinedVowels: [String: String] = [
|
||||||
|
"ㄧㄚ": Braille.d1246.rawValue,
|
||||||
|
"ㄧㄝ": Braille.d15.rawValue,
|
||||||
|
"ㄧㄞ": Braille.d1246.rawValue, // 此乃特例「崖」,依陸規審音處理。
|
||||||
|
"ㄧㄠ": Braille.d345.rawValue,
|
||||||
|
"ㄧㄡ": Braille.d1256.rawValue,
|
||||||
|
"ㄧㄢ": Braille.d146.rawValue,
|
||||||
|
"ㄧㄣ": Braille.d126.rawValue,
|
||||||
|
"ㄧㄤ": Braille.d1346.rawValue,
|
||||||
|
"ㄧㄥ": Braille.d16.rawValue,
|
||||||
|
"ㄨㄚ": Braille.d123456.rawValue,
|
||||||
|
"ㄨㄛ": Braille.d135.rawValue,
|
||||||
|
"ㄨㄞ": Braille.d13456.rawValue,
|
||||||
|
"ㄨㄟ": Braille.d2456.rawValue,
|
||||||
|
"ㄨㄢ": Braille.d12456.rawValue,
|
||||||
|
"ㄨㄣ": Braille.d25.rawValue,
|
||||||
|
"ㄨㄤ": Braille.d2356.rawValue,
|
||||||
|
"ㄨㄥ": Braille.d256.rawValue,
|
||||||
|
"ㄩㄝ": Braille.d23456.rawValue,
|
||||||
|
"ㄩㄢ": Braille.d12346.rawValue,
|
||||||
|
"ㄩㄣ": Braille.d456.rawValue,
|
||||||
|
"ㄩㄥ": Braille.d1456.rawValue,
|
||||||
|
]
|
||||||
|
|
||||||
|
let mapPunctuations: [String: String] = [
|
||||||
|
"。": Braille.d5.rawValue + Braille.d23.rawValue,
|
||||||
|
"·": Braille.d6.rawValue + Braille.d3.rawValue,
|
||||||
|
",": Braille.d5.rawValue,
|
||||||
|
";": Braille.d56.rawValue,
|
||||||
|
"、": Braille.d4.rawValue,
|
||||||
|
"?": Braille.d5.rawValue + Braille.d3.rawValue,
|
||||||
|
"!": Braille.d56.rawValue + Braille.d2.rawValue,
|
||||||
|
":": Braille.d36.rawValue,
|
||||||
|
"——": Braille.d6.rawValue + Braille.d36.rawValue,
|
||||||
|
"……": Braille.d5.rawValue + Braille.d5.rawValue + Braille.d5.rawValue,
|
||||||
|
"-": Braille.d36.rawValue,
|
||||||
|
"‧": Braille.d5.rawValue, // 著重號。
|
||||||
|
"*": Braille.d2356.rawValue + Braille.d35.rawValue,
|
||||||
|
"《": Braille.d5.rawValue + Braille.d36.rawValue,
|
||||||
|
"》": Braille.d36.rawValue + Braille.d2.rawValue,
|
||||||
|
"〈": Braille.d5.rawValue + Braille.d3.rawValue,
|
||||||
|
"〉": Braille.d6.rawValue + Braille.d2.rawValue,
|
||||||
|
"『": Braille.d45.rawValue + Braille.d45.rawValue,
|
||||||
|
"』": Braille.d45.rawValue + Braille.d45.rawValue,
|
||||||
|
"「": Braille.d45.rawValue,
|
||||||
|
"」": Braille.d45.rawValue,
|
||||||
|
"‘": Braille.d45.rawValue + Braille.d45.rawValue,
|
||||||
|
"’": Braille.d45.rawValue + Braille.d45.rawValue,
|
||||||
|
"“": Braille.d45.rawValue,
|
||||||
|
"”": Braille.d45.rawValue,
|
||||||
|
"(": Braille.d56.rawValue + Braille.d3.rawValue,
|
||||||
|
")": Braille.d6.rawValue + Braille.d23.rawValue,
|
||||||
|
"〔": Braille.d56.rawValue + Braille.d23.rawValue,
|
||||||
|
"〕": Braille.d56.rawValue + Braille.d23.rawValue,
|
||||||
|
"[": Braille.d56.rawValue + Braille.d23.rawValue,
|
||||||
|
"]": Braille.d56.rawValue + Braille.d23.rawValue,
|
||||||
|
// "{": "⠦", "}": "⠴", // 2018 國通標準並未定義花括弧。
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,109 @@
|
||||||
|
// (c) 2022 and onwards The vChewing Project (MIT-NTL License).
|
||||||
|
// ====================
|
||||||
|
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
|
||||||
|
// ... with NTL restriction stating that:
|
||||||
|
// No trademark license is granted to use the trade names, trademarks, service
|
||||||
|
// marks, or product names of Contributor, except as required to fulfill notice
|
||||||
|
// requirements defined in MIT License.
|
||||||
|
|
||||||
|
import Tekkon
|
||||||
|
|
||||||
|
public class BrailleSputnik {
|
||||||
|
public var standard: BrailleStandard
|
||||||
|
public init(standard: BrailleStandard) {
|
||||||
|
self.standard = standard
|
||||||
|
}
|
||||||
|
|
||||||
|
var staticData: BrailleProcessingUnit {
|
||||||
|
switch standard {
|
||||||
|
case .of1947: return Self.staticData1947
|
||||||
|
case .of2018: return Self.staticData2018
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static var sharedComposer = Tekkon.Composer("", arrange: .ofDachen, correction: true)
|
||||||
|
private static let staticData1947: BrailleProcessingUnit = BrailleProcessingUnit1947()
|
||||||
|
private static let staticData2018: BrailleProcessingUnit = BrailleProcessingUnit2018()
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension BrailleSputnik {
|
||||||
|
func convertToBraille(smashedPairs: [(key: String, value: String)], extraInsertion: (reading: String, cursor: Int)? = nil) -> String {
|
||||||
|
var convertedStack: [String?] = []
|
||||||
|
var processedKeysCount = 0
|
||||||
|
var extraInsertion = extraInsertion
|
||||||
|
smashedPairs.forEach { key, value in
|
||||||
|
let subKeys = key.split(separator: "\t")
|
||||||
|
switch subKeys.count {
|
||||||
|
case 0: return
|
||||||
|
case 1:
|
||||||
|
guard !key.isEmpty else { break }
|
||||||
|
let isPunctuation: Bool = key.first == "_" // 檢查是不是標點符號。
|
||||||
|
if isPunctuation {
|
||||||
|
convertedStack.append(convertPunctuationToBraille(value))
|
||||||
|
} else {
|
||||||
|
var key = key.description
|
||||||
|
fixToneOne(target: &key)
|
||||||
|
convertedStack.append(convertPhonabetReadingToBraille(key, value: value))
|
||||||
|
}
|
||||||
|
processedKeysCount += 1
|
||||||
|
default:
|
||||||
|
// 這種情形就是詞音配對不一致的典型情形,此時僅處理注音讀音。
|
||||||
|
subKeys.forEach { subKey in
|
||||||
|
var subKey = subKey.description
|
||||||
|
fixToneOne(target: &subKey)
|
||||||
|
convertedStack.append(convertPhonabetReadingToBraille(subKey))
|
||||||
|
processedKeysCount += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let theExtraInsertion = extraInsertion, processedKeysCount == theExtraInsertion.cursor {
|
||||||
|
convertedStack.append(convertPhonabetReadingToBraille(theExtraInsertion.reading))
|
||||||
|
extraInsertion = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return convertedStack.compactMap(\.?.description).joined()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func fixToneOne(target key: inout String) {
|
||||||
|
for char in key {
|
||||||
|
guard Tekkon.Phonabet(char.description).type != .null else { return }
|
||||||
|
}
|
||||||
|
if let lastChar = key.last?.description, Tekkon.Phonabet(lastChar).type != .intonation {
|
||||||
|
key += " "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertPunctuationToBraille(_ givenTarget: any StringProtocol) -> String? {
|
||||||
|
staticData.mapPunctuations[givenTarget.description]
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertPhonabetReadingToBraille(_ rawReading: any StringProtocol, value referredValue: String? = nil) -> String? {
|
||||||
|
var resultStack = ""
|
||||||
|
// 检查特殊情形。
|
||||||
|
guard !staticData.handleSpecialCases(target: &resultStack, value: referredValue) else { return resultStack }
|
||||||
|
Self.sharedComposer.clear()
|
||||||
|
rawReading.forEach { char in
|
||||||
|
Self.sharedComposer.receiveKey(fromPhonabet: char.description)
|
||||||
|
}
|
||||||
|
let consonant = Self.sharedComposer.consonant.value
|
||||||
|
let semivowel = Self.sharedComposer.semivowel.value
|
||||||
|
let vowel = Self.sharedComposer.vowel.value
|
||||||
|
let intonation = Self.sharedComposer.intonation.value
|
||||||
|
if !consonant.isEmpty {
|
||||||
|
resultStack.append(staticData.mapConsonants[consonant] ?? "")
|
||||||
|
}
|
||||||
|
let combinedVowels = Self.sharedComposer.semivowel.value + Self.sharedComposer.vowel.value
|
||||||
|
if combinedVowels.count == 2 {
|
||||||
|
resultStack.append(staticData.mapCombinedVowels[combinedVowels] ?? "")
|
||||||
|
} else {
|
||||||
|
resultStack.append(staticData.mapSemivowels[semivowel] ?? "")
|
||||||
|
resultStack.append(staticData.mapVowels[vowel] ?? "")
|
||||||
|
}
|
||||||
|
// 聲調處理。
|
||||||
|
if let intonationSpecialCaseMetResult = staticData.mapIntonationSpecialCases[vowel + intonation] {
|
||||||
|
resultStack.append(intonationSpecialCaseMetResult.last?.description ?? "")
|
||||||
|
} else {
|
||||||
|
resultStack.append(staticData.mapIntonations[intonation] ?? "")
|
||||||
|
}
|
||||||
|
return resultStack
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
// (c) 2022 and onwards The vChewing Project (MIT-NTL License).
|
||||||
|
// ====================
|
||||||
|
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
|
||||||
|
// ... with NTL restriction stating that:
|
||||||
|
// No trademark license is granted to use the trade names, trademarks, service
|
||||||
|
// marks, or product names of Contributor, except as required to fulfill notice
|
||||||
|
// requirements defined in MIT License.
|
||||||
|
|
||||||
|
@testable import BrailleSputnik
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
final class BrailleSputnikTests: XCTestCase {
|
||||||
|
func testBrailleConversion() throws {
|
||||||
|
// 大丘丘病了二丘丘瞧,三丘丘採藥四丘丘熬。
|
||||||
|
var rawReadingStr = "ㄉㄚˋ-ㄑㄧㄡ-ㄑㄧㄡ-ㄅㄧㄥˋ-ㄌㄜ˙-ㄦˋ-ㄑㄧㄡ-ㄑㄧㄡ-ㄑㄧㄠˊ-_,"
|
||||||
|
rawReadingStr += "-ㄙㄢ-ㄑㄧㄡ-ㄑㄧㄡ-ㄘㄞˇ-ㄧㄠˋ-ㄙˋ-ㄑㄧㄡ-ㄑㄧㄡ-ㄠˊ-_。"
|
||||||
|
let rawReadingArray: [(key: String, value: String)] = rawReadingStr.split(separator: "-").map {
|
||||||
|
let value: String = $0.first == "_" ? $0.last?.description ?? "" : ""
|
||||||
|
return (key: $0.description, value: value)
|
||||||
|
}
|
||||||
|
let processor = BrailleSputnik(standard: .of1947)
|
||||||
|
let result1947 = processor.convertToBraille(smashedPairs: rawReadingArray)
|
||||||
|
XCTAssertEqual(result1947, "⠙⠜⠐⠚⠎⠄⠚⠎⠄⠕⠽⠐⠉⠮⠁⠱⠐⠚⠎⠄⠚⠎⠄⠚⠪⠂⠆⠑⠧⠄⠚⠎⠄⠚⠎⠄⠚⠺⠈⠪⠐⠑⠐⠚⠎⠄⠚⠎⠄⠩⠂⠤⠀")
|
||||||
|
processor.standard = .of2018
|
||||||
|
let result2018 = processor.convertToBraille(smashedPairs: rawReadingArray)
|
||||||
|
XCTAssertEqual(result2018, "⠙⠔⠆⠅⠳⠁⠅⠳⠁⠃⠡⠆⠇⠢⠗⠆⠅⠳⠁⠅⠳⠁⠅⠜⠂⠐⠎⠧⠁⠅⠳⠁⠅⠳⠁⠉⠪⠄⠜⠆⠎⠆⠅⠳⠁⠅⠳⠁⠖⠂⠐⠆")
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ let package = Package(
|
||||||
.package(path: "../HangarRash_SwiftyCapsLockToggler"),
|
.package(path: "../HangarRash_SwiftyCapsLockToggler"),
|
||||||
.package(path: "../Jad_BookmarkManager"),
|
.package(path: "../Jad_BookmarkManager"),
|
||||||
.package(path: "../Qwertyyb_ShiftKeyUpChecker"),
|
.package(path: "../Qwertyyb_ShiftKeyUpChecker"),
|
||||||
|
.package(path: "../vChewing_BrailleSputnik"),
|
||||||
.package(path: "../vChewing_CandidateWindow"),
|
.package(path: "../vChewing_CandidateWindow"),
|
||||||
.package(path: "../vChewing_CocoaExtension"),
|
.package(path: "../vChewing_CocoaExtension"),
|
||||||
.package(path: "../vChewing_Hotenka"),
|
.package(path: "../vChewing_Hotenka"),
|
||||||
|
@ -38,6 +39,7 @@ let package = Package(
|
||||||
.target(
|
.target(
|
||||||
name: "MainAssembly",
|
name: "MainAssembly",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
|
.product(name: "BrailleSputnik", package: "vChewing_BrailleSputnik"),
|
||||||
.product(name: "BookmarkManager", package: "Jad_BookmarkManager"),
|
.product(name: "BookmarkManager", package: "Jad_BookmarkManager"),
|
||||||
.product(name: "CandidateWindow", package: "vChewing_CandidateWindow"),
|
.product(name: "CandidateWindow", package: "vChewing_CandidateWindow"),
|
||||||
.product(name: "CocoaExtension", package: "vChewing_CocoaExtension"),
|
.product(name: "CocoaExtension", package: "vChewing_CocoaExtension"),
|
||||||
|
|
|
@ -119,6 +119,7 @@
|
||||||
5B40113A28D71B8700A9D4CB /* vChewing_Uninstaller */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_Uninstaller; path = Packages/vChewing_Uninstaller; sourceTree = "<group>"; };
|
5B40113A28D71B8700A9D4CB /* vChewing_Uninstaller */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_Uninstaller; path = Packages/vChewing_Uninstaller; sourceTree = "<group>"; };
|
||||||
5B65B919284D0185007C558B /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
5B65B919284D0185007C558B /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||||
5B65FB322A9518C9007EEFB0 /* vChewing_MainAssembly */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_MainAssembly; path = Packages/vChewing_MainAssembly; sourceTree = "<group>"; };
|
5B65FB322A9518C9007EEFB0 /* vChewing_MainAssembly */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_MainAssembly; path = Packages/vChewing_MainAssembly; sourceTree = "<group>"; };
|
||||||
|
5B6CA6272B8A1C9200A85050 /* vChewing_BrailleSputnik */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_BrailleSputnik; path = Packages/vChewing_BrailleSputnik; sourceTree = "<group>"; };
|
||||||
5B70F4E52A0BE900005EA8C4 /* MenuIcon-TCVIM.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "MenuIcon-TCVIM.png"; sourceTree = "<group>"; };
|
5B70F4E52A0BE900005EA8C4 /* MenuIcon-TCVIM.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "MenuIcon-TCVIM.png"; sourceTree = "<group>"; };
|
||||||
5B70F4E62A0BE900005EA8C4 /* MenuIcon-SCVIM.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "MenuIcon-SCVIM.png"; sourceTree = "<group>"; };
|
5B70F4E62A0BE900005EA8C4 /* MenuIcon-SCVIM.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "MenuIcon-SCVIM.png"; sourceTree = "<group>"; };
|
||||||
5B70F4E72A0BE900005EA8C4 /* MenuIcon-SCVIM@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "MenuIcon-SCVIM@2x.png"; sourceTree = "<group>"; };
|
5B70F4E72A0BE900005EA8C4 /* MenuIcon-SCVIM@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "MenuIcon-SCVIM@2x.png"; sourceTree = "<group>"; };
|
||||||
|
@ -340,6 +341,7 @@
|
||||||
5BDB7A3028D47587001AC277 /* Jad_BookmarkManager */,
|
5BDB7A3028D47587001AC277 /* Jad_BookmarkManager */,
|
||||||
5BDB7A3428D47587001AC277 /* Qwertyyb_ShiftKeyUpChecker */,
|
5BDB7A3428D47587001AC277 /* Qwertyyb_ShiftKeyUpChecker */,
|
||||||
5BDB7A3528D47587001AC277 /* RMJay_LineReader */,
|
5BDB7A3528D47587001AC277 /* RMJay_LineReader */,
|
||||||
|
5B6CA6272B8A1C9200A85050 /* vChewing_BrailleSputnik */,
|
||||||
5BA8C30128DEFE4F004C5CC4 /* vChewing_CandidateWindow */,
|
5BA8C30128DEFE4F004C5CC4 /* vChewing_CandidateWindow */,
|
||||||
5B963C9B28D5BE4100DCEE88 /* vChewing_CocoaExtension */,
|
5B963C9B28D5BE4100DCEE88 /* vChewing_CocoaExtension */,
|
||||||
5BDB7A3228D47587001AC277 /* vChewing_Hotenka */,
|
5BDB7A3228D47587001AC277 /* vChewing_Hotenka */,
|
||||||
|
|
Loading…
Reference in New Issue