mirror of
https://github.com/mii443/AzooKeyKanaKanjiConverter.git
synced 2025-08-22 15:05:26 +00:00
Merge pull request #227 from azooKey/feat/mapped_input_style
feat: `.mapped(id)`を新たな入力スタイルとして導入し、カスタムローマ字かな変換テーブルに対応
This commit is contained in:
@ -62,7 +62,12 @@ extension Kana2Kanji {
|
||||
switch inputStyle {
|
||||
case .direct:
|
||||
dicdata = self.dicdataStore.getPredictionLOUDSDicdata(key: lastRuby)
|
||||
case .roman2kana:
|
||||
case .roman2kana, .mapped:
|
||||
let table = if case let .mapped(id) = inputStyle {
|
||||
InputStyleManager.shared.table(for: id)
|
||||
} else {
|
||||
InputStyleManager.shared.table(for: .defaultRomanToKana)
|
||||
}
|
||||
let roman = lastRuby.suffix(while: {String($0).onlyRomanAlphabet})
|
||||
if !roman.isEmpty {
|
||||
let ruby: Substring = lastRuby.dropLast(roman.count)
|
||||
@ -70,7 +75,7 @@ extension Kana2Kanji {
|
||||
dicdata = []
|
||||
break
|
||||
}
|
||||
let possibleNexts: [Substring] = DicdataStore.possibleNexts[String(roman), default: []].map {ruby + $0}
|
||||
let possibleNexts: [Substring] = table.possibleNexts[String(roman), default: []].map {ruby + $0}
|
||||
debug(#function, lastRuby, ruby, roman, possibleNexts, prepart, lastRubyCount)
|
||||
dicdata = possibleNexts.flatMap { self.dicdataStore.getPredictionLOUDSDicdata(key: $0) }
|
||||
} else {
|
||||
|
@ -336,10 +336,8 @@ import EfficientNGram
|
||||
/// 付加的な変換候補
|
||||
private func getTopLevelAdditionalCandidate(_ inputData: ComposingText, options: ConvertRequestOptions) -> [Candidate] {
|
||||
var candidates: [Candidate] = []
|
||||
if inputData.input.allSatisfy({$0.inputStyle == .roman2kana}) {
|
||||
if options.englishCandidateInRoman2KanaInput {
|
||||
candidates.append(contentsOf: self.getForeignPredictionCandidate(inputData: inputData, language: "en-US", penalty: -10))
|
||||
}
|
||||
if options.englishCandidateInRoman2KanaInput, inputData.input.allSatisfy({$0.character.isASCII}) {
|
||||
candidates.append(contentsOf: self.getForeignPredictionCandidate(inputData: inputData, language: "en-US", penalty: -10))
|
||||
}
|
||||
return candidates
|
||||
}
|
||||
|
@ -1027,15 +1027,4 @@ public final class DicdataStore {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
static let possibleNexts: [String: [String]] = {
|
||||
var results: [String: [String]] = [:]
|
||||
for (key, value) in Roman2Kana.katakanaChanges {
|
||||
for prefixCount in 0 ..< key.count where 0 < prefixCount {
|
||||
let prefix = String(key.prefix(prefixCount))
|
||||
results[prefix, default: []].append(value)
|
||||
}
|
||||
}
|
||||
return results
|
||||
}()
|
||||
}
|
||||
|
@ -97,9 +97,14 @@ struct TypoCorrectionGenerator: Sendable {
|
||||
switch item.inputStyle {
|
||||
case .direct:
|
||||
stablePrefix.append(contentsOf: item.string)
|
||||
case .roman2kana:
|
||||
case .roman2kana, .mapped:
|
||||
let table = if case let .mapped(id) = item.inputStyle {
|
||||
InputStyleManager.shared.table(for: id)
|
||||
} else {
|
||||
InputStyleManager.shared.table(for: .defaultRomanToKana)
|
||||
}
|
||||
var stableIndex = item.string.endIndex
|
||||
for suffix in Roman2Kana.unstableSuffixes {
|
||||
for suffix in table.unstableSuffixes {
|
||||
if item.string.hasSuffix(suffix) {
|
||||
stableIndex = min(stableIndex, item.string.endIndex - suffix.count)
|
||||
}
|
||||
@ -197,7 +202,7 @@ struct TypoCorrectionGenerator: Sendable {
|
||||
return result
|
||||
}
|
||||
}
|
||||
if (elements.allSatisfy {$0.inputStyle == .roman2kana}) {
|
||||
if (elements.allSatisfy {$0.inputStyle == .roman2kana || $0.inputStyle == .mapped(id: .defaultRomanToKana)}) {
|
||||
let dictionary: [String: [TypoCandidate]] = frozen ? [:] : Self.roman2KanaPossibleTypo
|
||||
if key.count > 1 {
|
||||
return dictionary[key, default: []]
|
||||
@ -210,7 +215,14 @@ struct TypoCorrectionGenerator: Sendable {
|
||||
return result
|
||||
}
|
||||
}
|
||||
return []
|
||||
// `.mapped`や、混ざっているケースでここに到達する
|
||||
return if elements.count == 1 {
|
||||
[
|
||||
TypoCandidate(inputElements: [elements.first!], weight: 0)
|
||||
]
|
||||
} else {
|
||||
[]
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate static let lengths = [0, 1]
|
||||
|
@ -185,10 +185,17 @@ public struct ComposingText: Sendable {
|
||||
// 例えばcovnertTargetが「あき|ょ」で、`[a, k, y, o]`まで見て「あきょ」になってしまった場合、「あき」がprefixとなる。
|
||||
// この場合、lastPrefix=1なので、1番目から現在までの入力をひらがな(suffix)で置き換える
|
||||
else if converted.hasPrefix(target) {
|
||||
// lastPrefixIndex: 「あ」までなので1
|
||||
// count: 「あきょ」までなので4
|
||||
// replaceCount: 3
|
||||
let replaceCount = count - lastPrefixIndex
|
||||
// suffix: 「あきょ」から「あ」を落とした分なので、「きょ」
|
||||
let suffix = converted.suffix(converted.count - lastPrefix.count)
|
||||
// lastPrefixIndexから現在のカウントまでをReplace
|
||||
self.input.removeSubrange(count - replaceCount ..< count)
|
||||
self.input.insert(contentsOf: suffix.map {InputElement(character: $0, inputStyle: CharacterUtils.isRomanLetter($0) ? .roman2kana : .direct)}, at: count - replaceCount)
|
||||
// suffix1文字ずつを入力に追加する
|
||||
// この結果として生じる文字列については、`frozen`で処理する
|
||||
self.input.insert(contentsOf: suffix.map {InputElement(character: $0, inputStyle: .frozen)}, at: count - replaceCount)
|
||||
|
||||
count -= replaceCount
|
||||
count += suffix.count
|
||||
@ -436,7 +443,9 @@ extension ComposingText {
|
||||
case .direct:
|
||||
return current + [newCharacter]
|
||||
case .roman2kana:
|
||||
return Roman2Kana.toHiragana(currentText: current, added: newCharacter)
|
||||
return InputStyleManager.shared.table(for: .defaultRomanToKana).toHiragana(currentText: current, added: newCharacter)
|
||||
case .mapped(let id):
|
||||
return InputStyleManager.shared.table(for: id).toHiragana(currentText: current, added: newCharacter)
|
||||
}
|
||||
}
|
||||
|
||||
@ -445,7 +454,9 @@ extension ComposingText {
|
||||
case .direct:
|
||||
convertTarget.append(newCharacter)
|
||||
case .roman2kana:
|
||||
convertTarget = Roman2Kana.toHiragana(currentText: convertTarget, added: newCharacter)
|
||||
convertTarget = InputStyleManager.shared.table(for: .defaultRomanToKana).toHiragana(currentText: convertTarget, added: newCharacter)
|
||||
case .mapped(let id):
|
||||
convertTarget = InputStyleManager.shared.table(for: id).toHiragana(currentText: convertTarget, added: newCharacter)
|
||||
}
|
||||
}
|
||||
|
||||
@ -486,9 +497,11 @@ extension ComposingText.InputElement: CustomDebugStringConvertible {
|
||||
public var debugDescription: String {
|
||||
switch self.inputStyle {
|
||||
case .direct:
|
||||
return "direct(\(character))"
|
||||
"direct(\(character))"
|
||||
case .roman2kana:
|
||||
return "roman2kana(\(character))"
|
||||
"roman2kana(\(character))"
|
||||
case .mapped(let id):
|
||||
"mapped(\(id); \(character))"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -500,7 +513,14 @@ extension ComposingText.ConvertTargetElement: CustomDebugStringConvertible {
|
||||
}
|
||||
extension InputStyle: CustomDebugStringConvertible {
|
||||
public var debugDescription: String {
|
||||
"." + self.rawValue
|
||||
switch self {
|
||||
case .direct:
|
||||
".direct"
|
||||
case .roman2kana:
|
||||
".roman2kana"
|
||||
case .mapped(let id):
|
||||
".mapped(\(id))"
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -0,0 +1,12 @@
|
||||
public enum InputStyle: Sendable, Equatable, Hashable {
|
||||
/// 入力された文字を直接入力するスタイル
|
||||
case direct
|
||||
/// ローマ字日本語入力とするスタイル
|
||||
case roman2kana
|
||||
/// カスタムローマ字かな変換テーブルなど、任意のマッピングを管理
|
||||
case mapped(id: InputTableID)
|
||||
|
||||
static var frozen: Self {
|
||||
.mapped(id: .empty)
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
import Foundation
|
||||
import SwiftUtils
|
||||
|
||||
final class InputStyleManager {
|
||||
nonisolated(unsafe) static let shared = InputStyleManager()
|
||||
|
||||
struct Table {
|
||||
init(hiraganaChanges: [[Character] : [Character]]) {
|
||||
self.hiraganaChanges = hiraganaChanges
|
||||
self.unstableSuffixes = hiraganaChanges.keys.flatMapSet { characters in
|
||||
characters.indices.map { i in
|
||||
Array(characters[...i])
|
||||
}
|
||||
}
|
||||
let katakanaChanges = Dictionary(uniqueKeysWithValues: hiraganaChanges.map { (String($0.key), String($0.value).toKatakana()) })
|
||||
self.katakanaChanges = katakanaChanges
|
||||
self.maxKeyCount = hiraganaChanges.lazy.map { $0.key.count }.max() ?? 0
|
||||
self.possibleNexts = {
|
||||
var results: [String: [String]] = [:]
|
||||
for (key, value) in katakanaChanges {
|
||||
for prefixCount in 0 ..< key.count where 0 < prefixCount {
|
||||
let prefix = String(key.prefix(prefixCount))
|
||||
results[prefix, default: []].append(value)
|
||||
}
|
||||
}
|
||||
return results
|
||||
}()
|
||||
}
|
||||
|
||||
let unstableSuffixes: Set<[Character]>
|
||||
let katakanaChanges: [String: String]
|
||||
let hiraganaChanges: [[Character]: [Character]]
|
||||
let maxKeyCount: Int
|
||||
let possibleNexts: [String: [String]]
|
||||
|
||||
static let empty = Table(hiraganaChanges: [:])
|
||||
|
||||
func toHiragana(currentText: [Character], added: Character) -> [Character] {
|
||||
for n in (0 ..< self.maxKeyCount).reversed() {
|
||||
if n == 0 {
|
||||
if let kana = self.hiraganaChanges[[added]] {
|
||||
return currentText + kana
|
||||
}
|
||||
} else {
|
||||
let last = currentText.suffix(n)
|
||||
if let kana = self.hiraganaChanges[last + [added]] {
|
||||
return currentText.prefix(currentText.count - last.count) + kana
|
||||
}
|
||||
}
|
||||
}
|
||||
return currentText + [added]
|
||||
}
|
||||
}
|
||||
|
||||
private var tables: [InputTableID: Table] = [:]
|
||||
|
||||
private init() {
|
||||
// デフォルトのテーブルは最初から追加しておく
|
||||
let defaultRomanToKana = Table(hiraganaChanges: Roman2KanaMaps.defaultRomanToKanaMap)
|
||||
let defaultAZIK = Table(hiraganaChanges: Roman2KanaMaps.defaultAzikMap)
|
||||
self.tables = [
|
||||
.empty: .empty,
|
||||
.defaultRomanToKana: defaultRomanToKana,
|
||||
.defaultAZIK: defaultAZIK
|
||||
]
|
||||
}
|
||||
|
||||
func table(for id: InputTableID) -> Table {
|
||||
switch id {
|
||||
case .defaultRomanToKana, .defaultAZIK, .empty:
|
||||
return self.tables[id]!
|
||||
case .custom(let url):
|
||||
if let table = self.tables[id] {
|
||||
return table
|
||||
} else if let table = try? Self.loadTable(from: url) {
|
||||
self.tables[id] = table
|
||||
return table
|
||||
} else {
|
||||
return .empty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func loadTable(from url: URL) throws -> Table {
|
||||
let content = try String(contentsOf: url, encoding: .utf8)
|
||||
var map: [[Character]: [Character]] = [:]
|
||||
for line in content.components(separatedBy: .newlines) {
|
||||
// 空行は無視
|
||||
guard !line.trimmingCharacters(in: .whitespaces).isEmpty else { continue }
|
||||
// `# `で始まる行はコメントとして明示的に無視
|
||||
guard !line.hasPrefix("# ") else { continue }
|
||||
let cols = line.split(separator: "\t")
|
||||
// 要素の無い行は無視
|
||||
guard cols.count >= 2 else { continue }
|
||||
let key = Array(String(cols[0]))
|
||||
let value = Array(String(cols[1]))
|
||||
map[key] = value
|
||||
}
|
||||
return Table(hiraganaChanges: map)
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
public import struct Foundation.URL
|
||||
|
||||
public enum InputTableID: Sendable, Equatable, Hashable {
|
||||
case defaultRomanToKana
|
||||
case defaultAZIK
|
||||
case empty
|
||||
case custom(URL)
|
||||
}
|
@ -1,349 +0,0 @@
|
||||
//
|
||||
// Roman2Kana.swift
|
||||
// Keyboard
|
||||
//
|
||||
// Created by ensan on 2020/09/24.
|
||||
// Copyright © 2020 ensan. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUtils
|
||||
|
||||
enum Roman2Kana {
|
||||
static let unstableSuffixes: Set<[Character]> = hiraganaChanges.keys.flatMapSet { characters in
|
||||
characters.indices.map { i in
|
||||
Array(characters[...i])
|
||||
}
|
||||
}
|
||||
static let katakanaChanges: [String: String] = Dictionary(uniqueKeysWithValues: hiraganaChanges.map { (String($0.key), String($0.value).toKatakana()) })
|
||||
static let hiraganaChanges: [[Character]: [Character]] = Dictionary(uniqueKeysWithValues: [
|
||||
"a": "あ",
|
||||
"xa": "ぁ",
|
||||
"la": "ぁ",
|
||||
"i": "い",
|
||||
"xi": "ぃ",
|
||||
"li": "ぃ",
|
||||
"u": "う",
|
||||
"wu": "う",
|
||||
"vu": "ゔ",
|
||||
"xu": "ぅ",
|
||||
"lu": "ぅ",
|
||||
"e": "え",
|
||||
"xe": "ぇ",
|
||||
"le": "ぇ",
|
||||
"o": "お",
|
||||
"xo": "ぉ",
|
||||
"lo": "ぉ",
|
||||
"ka": "か",
|
||||
"ca": "か",
|
||||
"ga": "が",
|
||||
"xka": "ゕ",
|
||||
"lka": "ゕ",
|
||||
"ki": "き",
|
||||
"gi": "ぎ",
|
||||
"ku": "く",
|
||||
"cu": "く",
|
||||
"gu": "ぐ",
|
||||
"ke": "け",
|
||||
"ge": "げ",
|
||||
"xke": "ゖ",
|
||||
"lke": "ゖ",
|
||||
"ko": "こ",
|
||||
"co": "こ",
|
||||
"go": "ご",
|
||||
"sa": "さ",
|
||||
"za": "ざ",
|
||||
"si": "し",
|
||||
"ci": "し",
|
||||
"shi": "し",
|
||||
"zi": "じ",
|
||||
"ji": "じ",
|
||||
"su": "す",
|
||||
"zu": "ず",
|
||||
"se": "せ",
|
||||
"ce": "せ",
|
||||
"ze": "ぜ",
|
||||
"so": "そ",
|
||||
"zo": "ぞ",
|
||||
"ta": "た",
|
||||
"da": "だ",
|
||||
"ti": "ち",
|
||||
"chi": "ち",
|
||||
"di": "ぢ",
|
||||
"tu": "つ",
|
||||
"tsu": "つ",
|
||||
"xtu": "っ",
|
||||
"ltu": "っ",
|
||||
"xtsu": "っ",
|
||||
"ltsu": "っ",
|
||||
"du": "づ",
|
||||
"te": "て",
|
||||
"de": "で",
|
||||
"to": "と",
|
||||
"do": "ど",
|
||||
"na": "な",
|
||||
"ni": "に",
|
||||
"nu": "ぬ",
|
||||
"ne": "ね",
|
||||
"no": "の",
|
||||
"ha": "は",
|
||||
"ba": "ば",
|
||||
"pa": "ぱ",
|
||||
"hi": "ひ",
|
||||
"bi": "び",
|
||||
"pi": "ぴ",
|
||||
"hu": "ふ",
|
||||
"fu": "ふ",
|
||||
"bu": "ぶ",
|
||||
"pu": "ぷ",
|
||||
"he": "へ",
|
||||
"be": "べ",
|
||||
"pe": "ぺ",
|
||||
"ho": "ほ",
|
||||
"bo": "ぼ",
|
||||
"po": "ぽ",
|
||||
"ma": "ま",
|
||||
"mi": "み",
|
||||
"mu": "む",
|
||||
"me": "め",
|
||||
"mo": "も",
|
||||
"ya": "や",
|
||||
"xya": "ゃ",
|
||||
"lya": "ゃ",
|
||||
"yu": "ゆ",
|
||||
"xyu": "ゅ",
|
||||
"lyu": "ゅ",
|
||||
"yo": "よ",
|
||||
"xyo": "ょ",
|
||||
"lyo": "ょ",
|
||||
"ra": "ら",
|
||||
"ri": "り",
|
||||
"ru": "る",
|
||||
"re": "れ",
|
||||
"ro": "ろ",
|
||||
"wa": "わ",
|
||||
"xwa": "ゎ",
|
||||
"lwa": "ゎ",
|
||||
"wyi": "ゐ",
|
||||
"wye": "ゑ",
|
||||
"wo": "を",
|
||||
"nn": "ん",
|
||||
"ye": "いぇ",
|
||||
"va": "ゔぁ",
|
||||
"vi": "ゔぃ",
|
||||
"ve": "ゔぇ",
|
||||
"vo": "ゔぉ",
|
||||
"kya": "きゃ",
|
||||
"kyu": "きゅ",
|
||||
"kye": "きぇ",
|
||||
"kyo": "きょ",
|
||||
"gya": "ぎゃ",
|
||||
"gyu": "ぎゅ",
|
||||
"gye": "ぎぇ",
|
||||
"gyo": "ぎょ",
|
||||
"qa": "くぁ",
|
||||
"kwa": "くぁ",
|
||||
"qwa": "くぁ",
|
||||
"qi": "くぃ",
|
||||
"kwi": "くぃ",
|
||||
"qwi": "くぃ",
|
||||
"qu": "くぅ",
|
||||
"kwu": "くぅ",
|
||||
"qwu": "くぅ",
|
||||
"qe": "くぇ",
|
||||
"kwe": "くぇ",
|
||||
"qwe": "くぇ",
|
||||
"qo": "くぉ",
|
||||
"kwo": "くぉ",
|
||||
"qwo": "くぉ",
|
||||
"gwa": "ぐぁ",
|
||||
"gwi": "ぐぃ",
|
||||
"gwu": "ぐぅ",
|
||||
"gwe": "ぐぇ",
|
||||
"gwo": "ぐぉ",
|
||||
"sha": "しゃ",
|
||||
"sya": "しゃ",
|
||||
"shu": "しゅ",
|
||||
"syu": "しゅ",
|
||||
"she": "しぇ",
|
||||
"sye": "しぇ",
|
||||
"sho": "しょ",
|
||||
"syo": "しょ",
|
||||
"ja": "じゃ",
|
||||
"zya": "じゃ",
|
||||
"jya": "じゃ",
|
||||
"jyi": "じぃ",
|
||||
"ju": "じゅ",
|
||||
"zyu": "じゅ",
|
||||
"jyu": "じゅ",
|
||||
"je": "じぇ",
|
||||
"zye": "じぇ",
|
||||
"jye": "じぇ",
|
||||
"jo": "じょ",
|
||||
"zyo": "じょ",
|
||||
"jyo": "じょ",
|
||||
"swa": "すぁ",
|
||||
"swi": "すぃ",
|
||||
"swu": "すぅ",
|
||||
"swe": "すぇ",
|
||||
"swo": "すぉ",
|
||||
"cha": "ちゃ",
|
||||
"cya": "ちゃ",
|
||||
"tya": "ちゃ",
|
||||
"tyi": "ちぃ",
|
||||
"cyi": "ちぃ",
|
||||
"chu": "ちゅ",
|
||||
"cyu": "ちゅ",
|
||||
"tyu": "ちゅ",
|
||||
"che": "ちぇ",
|
||||
"cye": "ちぇ",
|
||||
"tye": "ちぇ",
|
||||
"cho": "ちょ",
|
||||
"cyo": "ちょ",
|
||||
"tyo": "ちょ",
|
||||
"tsa": "つぁ",
|
||||
"tsi": "つぃ",
|
||||
"tse": "つぇ",
|
||||
"tso": "つぉ",
|
||||
"tha": "てゃ",
|
||||
"thi": "てぃ",
|
||||
"thu": "てゅ",
|
||||
"the": "てぇ",
|
||||
"tho": "てょ",
|
||||
"twa": "とぁ",
|
||||
"twi": "とぃ",
|
||||
"twu": "とぅ",
|
||||
"twe": "とぇ",
|
||||
"two": "とぉ",
|
||||
"dya": "ぢゃ",
|
||||
"dyi": "ぢぃ",
|
||||
"dyu": "ぢゅ",
|
||||
"dye": "ぢぇ",
|
||||
"dyo": "ぢょ",
|
||||
"dha": "でゃ",
|
||||
"dhi": "でぃ",
|
||||
"dhu": "でゅ",
|
||||
"dhe": "でぇ",
|
||||
"dho": "でょ",
|
||||
"dwa": "どぁ",
|
||||
"dwi": "どぃ",
|
||||
"dwu": "どぅ",
|
||||
"dwe": "どぇ",
|
||||
"dwo": "どぉ",
|
||||
"nya": "にゃ",
|
||||
"nyi": "にぃ",
|
||||
"nyu": "にゅ",
|
||||
"nye": "にぇ",
|
||||
"nyo": "にょ",
|
||||
"hya": "ひゃ",
|
||||
"hyi": "ひぃ",
|
||||
"hyu": "ひゅ",
|
||||
"hye": "ひぇ",
|
||||
"hyo": "ひょ",
|
||||
"bya": "びゃ",
|
||||
"byi": "びぃ",
|
||||
"byu": "びゅ",
|
||||
"bye": "びぇ",
|
||||
"byo": "びょ",
|
||||
"pya": "ぴゃ",
|
||||
"pyi": "ぴぃ",
|
||||
"pyu": "ぴゅ",
|
||||
"pye": "ぴぇ",
|
||||
"pyo": "ぴょ",
|
||||
"fa": "ふぁ",
|
||||
"hwa": "ふぁ",
|
||||
"fwa": "ふぁ",
|
||||
"fi": "ふぃ",
|
||||
"hwi": "ふぃ",
|
||||
"fwi": "ふぃ",
|
||||
"fwu": "ふぅ",
|
||||
"fe": "ふぇ",
|
||||
"hwe": "ふぇ",
|
||||
"fwe": "ふぇ",
|
||||
"fo": "ふぉ",
|
||||
"hwo": "ふぉ",
|
||||
"fwo": "ふぉ",
|
||||
"fya": "ふゃ",
|
||||
"fyu": "ふゅ",
|
||||
"fyo": "ふょ",
|
||||
"mya": "みゃ",
|
||||
"myi": "みぃ",
|
||||
"myu": "みゅ",
|
||||
"mye": "みぇ",
|
||||
"myo": "みょ",
|
||||
"rya": "りゃ",
|
||||
"ryi": "りぃ",
|
||||
"ryu": "りゅ",
|
||||
"rye": "りぇ",
|
||||
"ryo": "りょ",
|
||||
"wi": "うぃ",
|
||||
"we": "うぇ",
|
||||
"wha": "うぁ",
|
||||
"whi": "うぃ",
|
||||
"whu": "う",
|
||||
"whe": "うぇ",
|
||||
"who": "うぉ",
|
||||
"bb": "っb",
|
||||
"cc": "っc",
|
||||
"dd": "っd",
|
||||
"ff": "っf",
|
||||
"gg": "っg",
|
||||
"hh": "っh",
|
||||
"jj": "っj",
|
||||
"kk": "っk",
|
||||
"ll": "っl",
|
||||
"mm": "っm",
|
||||
"pp": "っp",
|
||||
"qq": "っq",
|
||||
"rr": "っr",
|
||||
"ss": "っs",
|
||||
"tt": "っt",
|
||||
"vv": "っv",
|
||||
"ww": "っw",
|
||||
"xx": "っx",
|
||||
"yy": "っy",
|
||||
"zz": "っz",
|
||||
"nb": "んb",
|
||||
"nc": "んc",
|
||||
"nd": "んd",
|
||||
"nf": "んf",
|
||||
"ng": "んg",
|
||||
"nh": "んh",
|
||||
"nj": "んj",
|
||||
"nk": "んk",
|
||||
"nl": "んl",
|
||||
"nm": "んm",
|
||||
"np": "んp",
|
||||
"nq": "んq",
|
||||
"nr": "んr",
|
||||
"ns": "んs",
|
||||
"nt": "んt",
|
||||
"nv": "んv",
|
||||
"nw": "んw",
|
||||
"nx": "んx",
|
||||
"nz": "んz",
|
||||
"xn": "ん",
|
||||
"zh": "←",
|
||||
"zj": "↓",
|
||||
"zk": "↑",
|
||||
"zl": "→"
|
||||
].map {(Array($0.key), Array($0.value))})
|
||||
|
||||
static let maxKeyCount = hiraganaChanges.lazy.map { $0.key.count }.max() ?? 0
|
||||
|
||||
static func toHiragana(currentText: [Character], added: Character) -> [Character] {
|
||||
for n in (0 ..< maxKeyCount).reversed() {
|
||||
if n == 0 {
|
||||
if let kana = Roman2Kana.hiraganaChanges[[added]] {
|
||||
return currentText + kana
|
||||
}
|
||||
} else {
|
||||
let last = currentText.suffix(n)
|
||||
if let kana = Roman2Kana.hiraganaChanges[last + [added]] {
|
||||
return currentText.prefix(currentText.count - last.count) + kana
|
||||
}
|
||||
}
|
||||
}
|
||||
return currentText + [added]
|
||||
}
|
||||
}
|
@ -0,0 +1,888 @@
|
||||
import Foundation
|
||||
|
||||
enum Roman2KanaMaps {
|
||||
static let defaultRomanToKanaMap: [[Character]: [Character]] = Dictionary(uniqueKeysWithValues: [
|
||||
"a": "あ",
|
||||
"xa": "ぁ",
|
||||
"la": "ぁ",
|
||||
"i": "い",
|
||||
"xi": "ぃ",
|
||||
"li": "ぃ",
|
||||
"u": "う",
|
||||
"wu": "う",
|
||||
"vu": "ゔ",
|
||||
"xu": "ぅ",
|
||||
"lu": "ぅ",
|
||||
"e": "え",
|
||||
"xe": "ぇ",
|
||||
"le": "ぇ",
|
||||
"o": "お",
|
||||
"xo": "ぉ",
|
||||
"lo": "ぉ",
|
||||
"ka": "か",
|
||||
"ca": "か",
|
||||
"ga": "が",
|
||||
"xka": "ゕ",
|
||||
"lka": "ゕ",
|
||||
"ki": "き",
|
||||
"gi": "ぎ",
|
||||
"ku": "く",
|
||||
"cu": "く",
|
||||
"gu": "ぐ",
|
||||
"ke": "け",
|
||||
"ge": "げ",
|
||||
"xke": "ゖ",
|
||||
"lke": "ゖ",
|
||||
"ko": "こ",
|
||||
"co": "こ",
|
||||
"go": "ご",
|
||||
"sa": "さ",
|
||||
"za": "ざ",
|
||||
"si": "し",
|
||||
"ci": "し",
|
||||
"shi": "し",
|
||||
"zi": "じ",
|
||||
"ji": "じ",
|
||||
"su": "す",
|
||||
"zu": "ず",
|
||||
"se": "せ",
|
||||
"ce": "せ",
|
||||
"ze": "ぜ",
|
||||
"so": "そ",
|
||||
"zo": "ぞ",
|
||||
"ta": "た",
|
||||
"da": "だ",
|
||||
"ti": "ち",
|
||||
"chi": "ち",
|
||||
"di": "ぢ",
|
||||
"tu": "つ",
|
||||
"tsu": "つ",
|
||||
"xtu": "っ",
|
||||
"ltu": "っ",
|
||||
"xtsu": "っ",
|
||||
"ltsu": "っ",
|
||||
"du": "づ",
|
||||
"te": "て",
|
||||
"de": "で",
|
||||
"to": "と",
|
||||
"do": "ど",
|
||||
"na": "な",
|
||||
"ni": "に",
|
||||
"nu": "ぬ",
|
||||
"ne": "ね",
|
||||
"no": "の",
|
||||
"ha": "は",
|
||||
"ba": "ば",
|
||||
"pa": "ぱ",
|
||||
"hi": "ひ",
|
||||
"bi": "び",
|
||||
"pi": "ぴ",
|
||||
"hu": "ふ",
|
||||
"fu": "ふ",
|
||||
"bu": "ぶ",
|
||||
"pu": "ぷ",
|
||||
"he": "へ",
|
||||
"be": "べ",
|
||||
"pe": "ぺ",
|
||||
"ho": "ほ",
|
||||
"bo": "ぼ",
|
||||
"po": "ぽ",
|
||||
"ma": "ま",
|
||||
"mi": "み",
|
||||
"mu": "む",
|
||||
"me": "め",
|
||||
"mo": "も",
|
||||
"ya": "や",
|
||||
"xya": "ゃ",
|
||||
"lya": "ゃ",
|
||||
"yu": "ゆ",
|
||||
"xyu": "ゅ",
|
||||
"lyu": "ゅ",
|
||||
"yo": "よ",
|
||||
"xyo": "ょ",
|
||||
"lyo": "ょ",
|
||||
"ra": "ら",
|
||||
"ri": "り",
|
||||
"ru": "る",
|
||||
"re": "れ",
|
||||
"ro": "ろ",
|
||||
"wa": "わ",
|
||||
"xwa": "ゎ",
|
||||
"lwa": "ゎ",
|
||||
"wyi": "ゐ",
|
||||
"wye": "ゑ",
|
||||
"wo": "を",
|
||||
"nn": "ん",
|
||||
"ye": "いぇ",
|
||||
"va": "ゔぁ",
|
||||
"vi": "ゔぃ",
|
||||
"ve": "ゔぇ",
|
||||
"vo": "ゔぉ",
|
||||
"kya": "きゃ",
|
||||
"kyu": "きゅ",
|
||||
"kye": "きぇ",
|
||||
"kyo": "きょ",
|
||||
"gya": "ぎゃ",
|
||||
"gyu": "ぎゅ",
|
||||
"gye": "ぎぇ",
|
||||
"gyo": "ぎょ",
|
||||
"qa": "くぁ",
|
||||
"kwa": "くぁ",
|
||||
"qwa": "くぁ",
|
||||
"qi": "くぃ",
|
||||
"kwi": "くぃ",
|
||||
"qwi": "くぃ",
|
||||
"qu": "くぅ",
|
||||
"kwu": "くぅ",
|
||||
"qwu": "くぅ",
|
||||
"qe": "くぇ",
|
||||
"kwe": "くぇ",
|
||||
"qwe": "くぇ",
|
||||
"qo": "くぉ",
|
||||
"kwo": "くぉ",
|
||||
"qwo": "くぉ",
|
||||
"gwa": "ぐぁ",
|
||||
"gwi": "ぐぃ",
|
||||
"gwu": "ぐぅ",
|
||||
"gwe": "ぐぇ",
|
||||
"gwo": "ぐぉ",
|
||||
"sha": "しゃ",
|
||||
"sya": "しゃ",
|
||||
"shu": "しゅ",
|
||||
"syu": "しゅ",
|
||||
"she": "しぇ",
|
||||
"sye": "しぇ",
|
||||
"sho": "しょ",
|
||||
"syo": "しょ",
|
||||
"ja": "じゃ",
|
||||
"zya": "じゃ",
|
||||
"jya": "じゃ",
|
||||
"jyi": "じぃ",
|
||||
"ju": "じゅ",
|
||||
"zyu": "じゅ",
|
||||
"jyu": "じゅ",
|
||||
"je": "じぇ",
|
||||
"zye": "じぇ",
|
||||
"jye": "じぇ",
|
||||
"jo": "じょ",
|
||||
"zyo": "じょ",
|
||||
"jyo": "じょ",
|
||||
"swa": "すぁ",
|
||||
"swi": "すぃ",
|
||||
"swu": "すぅ",
|
||||
"swe": "すぇ",
|
||||
"swo": "すぉ",
|
||||
"cha": "ちゃ",
|
||||
"cya": "ちゃ",
|
||||
"tya": "ちゃ",
|
||||
"tyi": "ちぃ",
|
||||
"cyi": "ちぃ",
|
||||
"chu": "ちゅ",
|
||||
"cyu": "ちゅ",
|
||||
"tyu": "ちゅ",
|
||||
"che": "ちぇ",
|
||||
"cye": "ちぇ",
|
||||
"tye": "ちぇ",
|
||||
"cho": "ちょ",
|
||||
"cyo": "ちょ",
|
||||
"tyo": "ちょ",
|
||||
"tsa": "つぁ",
|
||||
"tsi": "つぃ",
|
||||
"tse": "つぇ",
|
||||
"tso": "つぉ",
|
||||
"tha": "てゃ",
|
||||
"thi": "てぃ",
|
||||
"thu": "てゅ",
|
||||
"the": "てぇ",
|
||||
"tho": "てょ",
|
||||
"twa": "とぁ",
|
||||
"twi": "とぃ",
|
||||
"twu": "とぅ",
|
||||
"twe": "とぇ",
|
||||
"two": "とぉ",
|
||||
"dya": "ぢゃ",
|
||||
"dyi": "ぢぃ",
|
||||
"dyu": "ぢゅ",
|
||||
"dye": "ぢぇ",
|
||||
"dyo": "ぢょ",
|
||||
"dha": "でゃ",
|
||||
"dhi": "でぃ",
|
||||
"dhu": "でゅ",
|
||||
"dhe": "でぇ",
|
||||
"dho": "でょ",
|
||||
"dwa": "どぁ",
|
||||
"dwi": "どぃ",
|
||||
"dwu": "どぅ",
|
||||
"dwe": "どぇ",
|
||||
"dwo": "どぉ",
|
||||
"nya": "にゃ",
|
||||
"nyi": "にぃ",
|
||||
"nyu": "にゅ",
|
||||
"nye": "にぇ",
|
||||
"nyo": "にょ",
|
||||
"hya": "ひゃ",
|
||||
"hyi": "ひぃ",
|
||||
"hyu": "ひゅ",
|
||||
"hye": "ひぇ",
|
||||
"hyo": "ひょ",
|
||||
"bya": "びゃ",
|
||||
"byi": "びぃ",
|
||||
"byu": "びゅ",
|
||||
"bye": "びぇ",
|
||||
"byo": "びょ",
|
||||
"pya": "ぴゃ",
|
||||
"pyi": "ぴぃ",
|
||||
"pyu": "ぴゅ",
|
||||
"pye": "ぴぇ",
|
||||
"pyo": "ぴょ",
|
||||
"fa": "ふぁ",
|
||||
"hwa": "ふぁ",
|
||||
"fwa": "ふぁ",
|
||||
"fi": "ふぃ",
|
||||
"hwi": "ふぃ",
|
||||
"fwi": "ふぃ",
|
||||
"fwu": "ふぅ",
|
||||
"fe": "ふぇ",
|
||||
"hwe": "ふぇ",
|
||||
"fwe": "ふぇ",
|
||||
"fo": "ふぉ",
|
||||
"hwo": "ふぉ",
|
||||
"fwo": "ふぉ",
|
||||
"fya": "ふゃ",
|
||||
"fyu": "ふゅ",
|
||||
"fyo": "ふょ",
|
||||
"mya": "みゃ",
|
||||
"myi": "みぃ",
|
||||
"myu": "みゅ",
|
||||
"mye": "みぇ",
|
||||
"myo": "みょ",
|
||||
"rya": "りゃ",
|
||||
"ryi": "りぃ",
|
||||
"ryu": "りゅ",
|
||||
"rye": "りぇ",
|
||||
"ryo": "りょ",
|
||||
"wi": "うぃ",
|
||||
"we": "うぇ",
|
||||
"wha": "うぁ",
|
||||
"whi": "うぃ",
|
||||
"whu": "う",
|
||||
"whe": "うぇ",
|
||||
"who": "うぉ",
|
||||
"bb": "っb",
|
||||
"cc": "っc",
|
||||
"dd": "っd",
|
||||
"ff": "っf",
|
||||
"gg": "っg",
|
||||
"hh": "っh",
|
||||
"jj": "っj",
|
||||
"kk": "っk",
|
||||
"ll": "っl",
|
||||
"mm": "っm",
|
||||
"pp": "っp",
|
||||
"qq": "っq",
|
||||
"rr": "っr",
|
||||
"ss": "っs",
|
||||
"tt": "っt",
|
||||
"vv": "っv",
|
||||
"ww": "っw",
|
||||
"xx": "っx",
|
||||
"yy": "っy",
|
||||
"zz": "っz",
|
||||
"nb": "んb",
|
||||
"nc": "んc",
|
||||
"nd": "んd",
|
||||
"nf": "んf",
|
||||
"ng": "んg",
|
||||
"nh": "んh",
|
||||
"nj": "んj",
|
||||
"nk": "んk",
|
||||
"nl": "んl",
|
||||
"nm": "んm",
|
||||
"np": "んp",
|
||||
"nq": "んq",
|
||||
"nr": "んr",
|
||||
"ns": "んs",
|
||||
"nt": "んt",
|
||||
"nv": "んv",
|
||||
"nw": "んw",
|
||||
"nx": "んx",
|
||||
"nz": "んz",
|
||||
"xn": "ん",
|
||||
"zh": "←",
|
||||
"zj": "↓",
|
||||
"zk": "↑",
|
||||
"zl": "→"
|
||||
].map {(Array($0.key), Array($0.value))})
|
||||
|
||||
static let defaultAzikMap: [[Character]: [Character]] = Dictionary(uniqueKeysWithValues: [
|
||||
"a": "あ",
|
||||
"i": "い",
|
||||
"u": "う",
|
||||
"e": "え",
|
||||
"o": "お",
|
||||
"ka": "か",
|
||||
"ki": "き",
|
||||
"ku": "く",
|
||||
"ke": "け",
|
||||
"ko": "こ",
|
||||
"sa": "さ",
|
||||
"si": "し",
|
||||
"su": "す",
|
||||
"se": "せ",
|
||||
"so": "そ",
|
||||
"ta": "た",
|
||||
"ti": "ち",
|
||||
"tu": "つ",
|
||||
"te": "て",
|
||||
"to": "と",
|
||||
"na": "な",
|
||||
"ni": "に",
|
||||
"nu": "ぬ",
|
||||
"ne": "ね",
|
||||
"no": "の",
|
||||
"ha": "は",
|
||||
"hi": "ひ",
|
||||
"hu": "ふ",
|
||||
"he": "へ",
|
||||
"ho": "ほ",
|
||||
"ma": "ま",
|
||||
"mi": "み",
|
||||
"mu": "む",
|
||||
"me": "め",
|
||||
"mo": "も",
|
||||
"ya": "や",
|
||||
"yu": "ゆ",
|
||||
"yo": "よ",
|
||||
"ra": "ら",
|
||||
"ri": "り",
|
||||
"ru": "る",
|
||||
"re": "れ",
|
||||
"ro": "ろ",
|
||||
"wa": "わ",
|
||||
"wi": "うぃ",
|
||||
"we": "うぇ",
|
||||
"wo": "を",
|
||||
"ga": "が",
|
||||
"gi": "ぎ",
|
||||
"gu": "ぐ",
|
||||
"ge": "げ",
|
||||
"go": "ご",
|
||||
"za": "ざ",
|
||||
"zi": "じ",
|
||||
"zu": "ず",
|
||||
"ze": "ぜ",
|
||||
"zo": "ぞ",
|
||||
"da": "だ",
|
||||
"di": "ぢ",
|
||||
"du": "づ",
|
||||
"de": "で",
|
||||
"do": "ど",
|
||||
"ba": "ば",
|
||||
"bi": "び",
|
||||
"bu": "ぶ",
|
||||
"be": "べ",
|
||||
"bo": "ぼ",
|
||||
"pa": "ぱ",
|
||||
"pi": "ぴ",
|
||||
"pu": "ぷ",
|
||||
"pe": "ぺ",
|
||||
"po": "ぽ",
|
||||
"kya": "きゃ",
|
||||
"kyu": "きゅ",
|
||||
"kye": "きぇ",
|
||||
"kyo": "きょ",
|
||||
"kga": "きゃ",
|
||||
"kgu": "きゅ",
|
||||
"kge": "きぇ",
|
||||
"kgo": "きょ",
|
||||
"sya": "しゃ",
|
||||
"syu": "しゅ",
|
||||
"sye": "しぇ",
|
||||
"syo": "しょ",
|
||||
"xa": "しゃ",
|
||||
"xu": "しゅ",
|
||||
"xe": "しぇ",
|
||||
"xo": "しょ",
|
||||
"tya": "ちゃ",
|
||||
"tyu": "ちゅ",
|
||||
"tye": "ちぇ",
|
||||
"tyo": "ちょ",
|
||||
"ca": "ちゃ",
|
||||
"cu": "ちゅ",
|
||||
"ce": "ちぇ",
|
||||
"co": "ちょ",
|
||||
"nya": "にゃ",
|
||||
"nyu": "にゅ",
|
||||
"nye": "にぇ",
|
||||
"nyo": "にょ",
|
||||
"nga": "にゃ",
|
||||
"ngu": "にゅ",
|
||||
"nge": "にぇ",
|
||||
"ngo": "にょ",
|
||||
"hya": "ひゃ",
|
||||
"hyu": "ひゅ",
|
||||
"hye": "ひぇ",
|
||||
"hyo": "ひょ",
|
||||
"hga": "ひゃ",
|
||||
"hgu": "ひゅ",
|
||||
"hge": "ひぇ",
|
||||
"hgo": "ひょ",
|
||||
"mya": "みゃ",
|
||||
"myu": "みゅ",
|
||||
"mye": "みぇ",
|
||||
"myo": "みょ",
|
||||
"mga": "みゃ",
|
||||
"mgu": "みゅ",
|
||||
"mge": "みぇ",
|
||||
"mgo": "みょ",
|
||||
"rya": "りゃ",
|
||||
"ryu": "りゅ",
|
||||
"rye": "りぇ",
|
||||
"ryo": "りょ",
|
||||
"gya": "ぎゃ",
|
||||
"gyu": "ぎゅ",
|
||||
"gye": "ぎぇ",
|
||||
"gyo": "ぎょ",
|
||||
"zya": "じゃ",
|
||||
"zyu": "じゅ",
|
||||
"zye": "じぇ",
|
||||
"zyo": "じょ",
|
||||
"ja": "じゃ",
|
||||
"ju": "じゅ",
|
||||
"je": "じぇ",
|
||||
"jo": "じょ",
|
||||
"bya": "びゃ",
|
||||
"byu": "びゅ",
|
||||
"bye": "びぇ",
|
||||
"byo": "びょ",
|
||||
"pya": "ぴゃ",
|
||||
"pyu": "ぴゅ",
|
||||
"pye": "ぴぇ",
|
||||
"pyo": "ぴょ",
|
||||
"pga": "ぴゃ",
|
||||
"pgu": "ぴゅ",
|
||||
"pge": "ぴぇ",
|
||||
"pgo": "ぴょ",
|
||||
"fa": "ふぁ",
|
||||
"fi": "ふぃ",
|
||||
"fu": "ふ",
|
||||
"fe": "ふぇ",
|
||||
"fo": "ふぉ",
|
||||
"va": "ヴぁ",
|
||||
"vi": "ヴぃ",
|
||||
"vu": "ヴ",
|
||||
"ve": "ヴぇ",
|
||||
"vo": "ヴぉ",
|
||||
"tgi": "てぃ",
|
||||
"tgu": "とぅ",
|
||||
"dci": "でぃ",
|
||||
"dcu": "どぅ",
|
||||
"wso": "うぉ",
|
||||
"la": "ぁ",
|
||||
"li": "ぃ",
|
||||
"lu": "ぅ",
|
||||
"le": "ぇ",
|
||||
"lo": "ぉ",
|
||||
"lya": "ゃ",
|
||||
"lyu": "ゅ",
|
||||
"lyo": "ょ",
|
||||
";": "っ",
|
||||
"q": "ん",
|
||||
"nn": "ん",
|
||||
":": "ー",
|
||||
"z。": "…",
|
||||
"z、": "‥",
|
||||
"zー": "〜",
|
||||
"z「": "『",
|
||||
"z」": "』",
|
||||
"kz": "かん",
|
||||
"kn": "かん",
|
||||
"kk": "きん",
|
||||
"kj": "くん",
|
||||
"kd": "けん",
|
||||
"kl": "こん",
|
||||
"sz": "さん",
|
||||
"sn": "さん",
|
||||
"sk": "しん",
|
||||
"sj": "すん",
|
||||
"sd": "せん",
|
||||
"sl": "そん",
|
||||
"tz": "たん",
|
||||
"tn": "たん",
|
||||
"tk": "ちん",
|
||||
"tj": "つん",
|
||||
"td": "てん",
|
||||
"tl": "とん",
|
||||
"nz": "なん",
|
||||
"nk": "にん",
|
||||
"nj": "ぬん",
|
||||
"nd": "ねん",
|
||||
"nl": "のん",
|
||||
"hz": "はん",
|
||||
"hn": "はん",
|
||||
"hk": "ひん",
|
||||
"hj": "ふん",
|
||||
"hd": "へん",
|
||||
"hl": "ほん",
|
||||
"mz": "まん",
|
||||
"mk": "みん",
|
||||
"mj": "むん",
|
||||
"md": "めん",
|
||||
"ml": "もん",
|
||||
"yz": "やん",
|
||||
"yn": "やん",
|
||||
"yj": "ゆん",
|
||||
"yl": "よん",
|
||||
"rz": "らん",
|
||||
"rn": "らん",
|
||||
"rk": "りん",
|
||||
"rj": "るん",
|
||||
"rd": "れん",
|
||||
"rl": "ろん",
|
||||
"wz": "わん",
|
||||
"wn": "わん",
|
||||
"wk": "うぃん",
|
||||
"wd": "うぇん",
|
||||
"wl": "うぉん",
|
||||
"gz": "がん",
|
||||
"gn": "がん",
|
||||
"gk": "ぎん",
|
||||
"gj": "ぐん",
|
||||
"gd": "げん",
|
||||
"gl": "ごん",
|
||||
"zz": "ざん",
|
||||
"zn": "ざん",
|
||||
"zk": "じん",
|
||||
"zj": "ずん",
|
||||
"zd": "ぜん",
|
||||
"zl": "ぞん",
|
||||
"dz": "だん",
|
||||
"dn": "だん",
|
||||
"dk": "ぢん",
|
||||
"dj": "づん",
|
||||
"dd": "でん",
|
||||
"dl": "どん",
|
||||
"bz": "ばん",
|
||||
"bn": "ばん",
|
||||
"bk": "びん",
|
||||
"bj": "ぶん",
|
||||
"bd": "べん",
|
||||
"bl": "ぼん",
|
||||
"pz": "ぱん",
|
||||
"pn": "ぱん",
|
||||
"pk": "ぴん",
|
||||
"pj": "ぷん",
|
||||
"pd": "ぺん",
|
||||
"pl": "ぽん",
|
||||
"kyz": "きゃん",
|
||||
"kyn": "きゃん",
|
||||
"kyj": "きゅん",
|
||||
"kyd": "きぇん",
|
||||
"kyl": "きょん",
|
||||
"kgz": "きゃん",
|
||||
"kgn": "きゃん",
|
||||
"kgj": "きゅん",
|
||||
"kgd": "きぇん",
|
||||
"kgl": "きょん",
|
||||
"syz": "しゃん",
|
||||
"syn": "しゃん",
|
||||
"syj": "しゅん",
|
||||
"syd": "しぇん",
|
||||
"syl": "しょん",
|
||||
"xz": "しゃん",
|
||||
"xn": "しゃん",
|
||||
"xj": "しゅん",
|
||||
"xd": "しぇん",
|
||||
"xl": "しょん",
|
||||
"tyz": "ちゃん",
|
||||
"tyn": "ちゃん",
|
||||
"tyj": "ちゅん",
|
||||
"tyd": "ちぇん",
|
||||
"tyl": "ちょん",
|
||||
"cz": "ちゃん",
|
||||
"cn": "ちゃん",
|
||||
"cj": "ちゅん",
|
||||
"cd": "ちぇん",
|
||||
"cl": "ちょん",
|
||||
"nyz": "にゃん",
|
||||
"nyn": "にゃん",
|
||||
"nyj": "にゅん",
|
||||
"nyd": "にぇん",
|
||||
"nyl": "にょん",
|
||||
"ngz": "にゃん",
|
||||
"ngn": "にゃん",
|
||||
"ngj": "にゅん",
|
||||
"ngd": "にぇん",
|
||||
"ngl": "にょん",
|
||||
"hyz": "ひゃん",
|
||||
"hyn": "ひゃん",
|
||||
"hyj": "ひゅん",
|
||||
"hyd": "ひぇん",
|
||||
"hyl": "ひょん",
|
||||
"hgz": "ひゃん",
|
||||
"hgn": "ひゃん",
|
||||
"hgj": "ひゅん",
|
||||
"hgd": "ひぇん",
|
||||
"hgl": "ひょん",
|
||||
"myz": "みゃん",
|
||||
"myn": "みゃん",
|
||||
"myj": "みゅん",
|
||||
"myd": "みぇん",
|
||||
"myl": "みょん",
|
||||
"mgz": "みゃん",
|
||||
"mgn": "みゃん",
|
||||
"mgj": "みゅん",
|
||||
"mgd": "みぇん",
|
||||
"mgl": "みょん",
|
||||
"ryz": "りゃん",
|
||||
"ryn": "りゃん",
|
||||
"ryj": "りゅん",
|
||||
"ryd": "りぇん",
|
||||
"ryl": "りょん",
|
||||
"gyz": "ぎゃん",
|
||||
"gyn": "ぎゃん",
|
||||
"gyj": "ぎゅん",
|
||||
"gyd": "ぎぇん",
|
||||
"gyl": "ぎょん",
|
||||
"zyz": "じゃん",
|
||||
"zyn": "じゃん",
|
||||
"zyj": "じゅん",
|
||||
"zyd": "じぇん",
|
||||
"zyl": "じょん",
|
||||
"jz": "じゃん",
|
||||
"jn": "じゃん",
|
||||
"jj": "じゅん",
|
||||
"jd": "じぇん",
|
||||
"jl": "じょん",
|
||||
"byz": "びゃん",
|
||||
"byn": "びゃん",
|
||||
"byj": "びゅん",
|
||||
"byd": "びぇん",
|
||||
"byl": "びょん",
|
||||
"pyz": "ぴゃん",
|
||||
"pyn": "ぴゃん",
|
||||
"pyj": "ぴゅん",
|
||||
"pyd": "ぴぇん",
|
||||
"pyl": "ぴょん",
|
||||
"pgz": "ぴゃん",
|
||||
"pgn": "ぴゃん",
|
||||
"pgj": "ぴゅん",
|
||||
"pgd": "ぴぇん",
|
||||
"pgl": "ぴょん",
|
||||
"fz": "ふぁん",
|
||||
"fn": "ふぁん",
|
||||
"fk": "ふぃん",
|
||||
"fj": "ふん",
|
||||
"fd": "ふぇん",
|
||||
"fl": "ふぉん",
|
||||
"vz": "ゔぁん",
|
||||
"vn": "ゔぁん",
|
||||
"vk": "ゔぃん",
|
||||
"vj": "ゔん",
|
||||
"vd": "ゔぇん",
|
||||
"vl": "ゔぉん",
|
||||
"tgk": "てぃん",
|
||||
"tgj": "とぅん",
|
||||
"dck": "でぃん",
|
||||
"dcj": "どぅん",
|
||||
"lz": "ぁん",
|
||||
"ln": "ぁん",
|
||||
"lk": "ぃん",
|
||||
"ld": "ぇん",
|
||||
"ll": "ぉん",
|
||||
"lyz": "ゃん",
|
||||
"lyn": "ゃん",
|
||||
"lyj": "ゅん",
|
||||
"lyl": "ょん",
|
||||
"kq": "かい",
|
||||
"kh": "くう",
|
||||
"kw": "けい",
|
||||
"kp": "こう",
|
||||
"sq": "さい",
|
||||
"sh": "すう",
|
||||
"sw": "せい",
|
||||
"sp": "そう",
|
||||
"tq": "たい",
|
||||
"th": "つう",
|
||||
"tw": "てい",
|
||||
"tp": "とう",
|
||||
"nq": "ない",
|
||||
"nh": "ぬう",
|
||||
"nw": "ねい",
|
||||
"np": "のう",
|
||||
"hq": "はい",
|
||||
"hh": "ふう",
|
||||
"hw": "へい",
|
||||
"hp": "ほう",
|
||||
"mq": "まい",
|
||||
"mh": "むう",
|
||||
"mw": "めい",
|
||||
"mp": "もう",
|
||||
"yq": "やい",
|
||||
"yh": "ゆう",
|
||||
"yp": "よう",
|
||||
"rq": "らい",
|
||||
"rh": "るう",
|
||||
"rw": "れい",
|
||||
"rp": "ろう",
|
||||
"gq": "がい",
|
||||
"gh": "ぐう",
|
||||
"gw": "げい",
|
||||
"gp": "ごう",
|
||||
"zq": "ざい",
|
||||
"zh": "ずう",
|
||||
"zw": "ぜい",
|
||||
"zp": "ぞう",
|
||||
"dq": "だい",
|
||||
"dh": "づう",
|
||||
"dw": "でい",
|
||||
"dp": "どう",
|
||||
"bq": "ばい",
|
||||
"bh": "ぶう",
|
||||
"bw": "べい",
|
||||
"bp": "ぼう",
|
||||
"pq": "ぱい",
|
||||
"ph": "ぷう",
|
||||
"pw": "ぺい",
|
||||
"pp": "ぽう",
|
||||
"kyq": "きゃい",
|
||||
"kyh": "きゅう",
|
||||
"kyw": "きぇい",
|
||||
"kyp": "きょう",
|
||||
"kgq": "きゃい",
|
||||
"kgh": "きゅう",
|
||||
"kgw": "きぇい",
|
||||
"kgp": "きょう",
|
||||
"syq": "しゃい",
|
||||
"syh": "しゅう",
|
||||
"syw": "しぇい",
|
||||
"syp": "しょう",
|
||||
"xq": "しゃい",
|
||||
"xh": "しゅう",
|
||||
"xw": "しぇい",
|
||||
"xp": "しょう",
|
||||
"tyq": "ちゃい",
|
||||
"tyh": "ちゅう",
|
||||
"tyw": "ちぇい",
|
||||
"typ": "ちょう",
|
||||
"cq": "ちゃい",
|
||||
"ch": "ちゅう",
|
||||
"cw": "ちぇい",
|
||||
"cp": "ちょう",
|
||||
"nyq": "にゃい",
|
||||
"nyh": "にゅう",
|
||||
"nyw": "にぇい",
|
||||
"nyp": "にょう",
|
||||
"ngq": "にゃい",
|
||||
"ngh": "にゅう",
|
||||
"ngw": "にぇい",
|
||||
"ngp": "にょう",
|
||||
"hyq": "ひゃい",
|
||||
"hyh": "ひゅう",
|
||||
"hyw": "ひぇい",
|
||||
"hyp": "ひょう",
|
||||
"hgq": "ひゃい",
|
||||
"hgh": "ひゅう",
|
||||
"hgw": "ひぇい",
|
||||
"hgp": "ひょう",
|
||||
"myq": "みゃい",
|
||||
"myh": "みゅう",
|
||||
"myw": "みぇい",
|
||||
"myp": "みょう",
|
||||
"mgq": "みゃい",
|
||||
"mgh": "みゅう",
|
||||
"mgw": "みぇい",
|
||||
"mgp": "みょう",
|
||||
"ryq": "りゃい",
|
||||
"ryh": "りゅう",
|
||||
"ryw": "りぇい",
|
||||
"ryp": "りょう",
|
||||
"gyq": "ぎゃい",
|
||||
"gyh": "ぎゅう",
|
||||
"gyw": "ぎぇい",
|
||||
"gyp": "ぎょう",
|
||||
"zyq": "じゃい",
|
||||
"zyh": "じゅう",
|
||||
"zyw": "じぇい",
|
||||
"zyp": "じょう",
|
||||
"jq": "じゃい",
|
||||
"jh": "じゅう",
|
||||
"jw": "じぇい",
|
||||
"jp": "じょう",
|
||||
"byq": "びゃい",
|
||||
"byh": "びゅう",
|
||||
"byw": "びぇい",
|
||||
"byp": "びょう",
|
||||
"pyq": "ぴゃい",
|
||||
"pyh": "ぴゅう",
|
||||
"pyw": "ぴぇい",
|
||||
"pyp": "ぴょう",
|
||||
"pgq": "ぴゃい",
|
||||
"pgh": "ぴゅう",
|
||||
"pgw": "ぴぇい",
|
||||
"pgp": "ぴょう",
|
||||
"fq": "ふぁい",
|
||||
"fh": "ふう",
|
||||
"fw": "ふぇい",
|
||||
"fp": "ふぉー",
|
||||
"vq": "ゔぁい",
|
||||
"vh": "ゔー",
|
||||
"vw": "ゔぇい",
|
||||
"vp": "ゔぉー",
|
||||
"tgh": "とぅー",
|
||||
"dch": "どぅー",
|
||||
"wq": "わい",
|
||||
"ww": "うぇい",
|
||||
"wp": "うぉー",
|
||||
"lq": "ぁい",
|
||||
"lh": "ぅう",
|
||||
"lw": "ぇい",
|
||||
"lp": "ぉう",
|
||||
"lyq": "ゃい",
|
||||
"lyh": "ゅう",
|
||||
"lyp": "ょう",
|
||||
"kf": "き",
|
||||
"jf": "じゅ",
|
||||
"hf": "ふ",
|
||||
"yf": "ゆ",
|
||||
"mf": "む",
|
||||
"nf": "ぬ",
|
||||
"df": "で",
|
||||
"cf": "ちぇ",
|
||||
"pf": "ぽん",
|
||||
"wf": "わい",
|
||||
"sf": "さい",
|
||||
"ss": "せい",
|
||||
"zc": "ざ",
|
||||
"zv": "ざい",
|
||||
"zf": "ぜ",
|
||||
"zx": "ぜい",
|
||||
"kt": "こと",
|
||||
"wt": "わた",
|
||||
"km": "かも",
|
||||
"sr": "する",
|
||||
"rr": "られ",
|
||||
"nb": "ねば",
|
||||
"nt": "にち",
|
||||
"st": "した",
|
||||
"mn": "もの",
|
||||
"tm": "ため",
|
||||
"tr": "たら",
|
||||
"zr": "ざる",
|
||||
"bt": "びと",
|
||||
"dt": "だち",
|
||||
"tt": "たち",
|
||||
"ms": "ます",
|
||||
"dm": "でも",
|
||||
"nr": "なる",
|
||||
"mt": "また",
|
||||
"gr": "がら",
|
||||
"wr": "われ",
|
||||
"ht": "ひと",
|
||||
"ds": "です",
|
||||
"kr": "から",
|
||||
"yr": "よる",
|
||||
"tb": "たび",
|
||||
"gt": "ごと",
|
||||
].map {(Array($0.key), Array($0.value))})
|
||||
}
|
@ -5,13 +5,6 @@
|
||||
// Created by ensan on 2023/04/30.
|
||||
//
|
||||
|
||||
public enum InputStyle: String, Sendable {
|
||||
/// 入力された文字を直接入力するスタイル
|
||||
case direct = "direct"
|
||||
/// ローマ字日本語入力とするスタイル
|
||||
case roman2kana = "roman"
|
||||
}
|
||||
|
||||
public enum KeyboardLanguage: String, Codable, Equatable, Sendable {
|
||||
case en_US
|
||||
case ja_JP
|
||||
|
@ -25,11 +25,6 @@ public enum CharacterUtils {
|
||||
kogakiKana.contains(character)
|
||||
}
|
||||
|
||||
/// ローマ字(a-z, A-Zか否か)
|
||||
@inlinable public static func isRomanLetter(_ character: Character) -> Bool {
|
||||
character.isASCII && character.isCased
|
||||
}
|
||||
|
||||
/// 自分が小書きであれば該当する文字を返す。
|
||||
public static func kogaki(_ character: Character) -> Character {
|
||||
switch character {
|
||||
|
@ -140,7 +140,7 @@ final class ComposingTextTests: XCTestCase {
|
||||
ComposingText.InputElement(character: "a", inputStyle: .roman2kana),
|
||||
ComposingText.InputElement(character: "k", inputStyle: .roman2kana),
|
||||
ComposingText.InputElement(character: "a", inputStyle: .roman2kana),
|
||||
ComposingText.InputElement(character: "ふ", inputStyle: .direct)
|
||||
ComposingText.InputElement(character: "ふ", inputStyle: .frozen)
|
||||
])
|
||||
XCTAssertEqual(c.convertTarget, "あかふ")
|
||||
XCTAssertEqual(c.convertTargetCursorPosition, 3)
|
||||
|
@ -0,0 +1,28 @@
|
||||
@testable import KanaKanjiConverterModule
|
||||
import XCTest
|
||||
|
||||
final class InputStyleManagerTests: XCTestCase {
|
||||
func testCustomTableLoading() throws {
|
||||
let url = FileManager.default.temporaryDirectory.appendingPathComponent("custom.tsv")
|
||||
try "a\tあ\nka\tか\n".write(to: url, atomically: true, encoding: .utf8)
|
||||
let table = InputStyleManager.shared.table(for: .custom(url))
|
||||
XCTAssertEqual(table.toHiragana(currentText: [], added: "a"), Array("あ"))
|
||||
XCTAssertEqual(table.toHiragana(currentText: ["k"], added: "a"), Array("か"))
|
||||
}
|
||||
|
||||
func testCustomTableLoadingWithBlankLines() throws {
|
||||
let url = FileManager.default.temporaryDirectory.appendingPathComponent("custom.tsv")
|
||||
try "a\tあ\n\n\nka\tか\n".write(to: url, atomically: true, encoding: .utf8)
|
||||
let table = InputStyleManager.shared.table(for: .custom(url))
|
||||
XCTAssertEqual(table.toHiragana(currentText: [], added: "a"), Array("あ"))
|
||||
XCTAssertEqual(table.toHiragana(currentText: ["k"], added: "a"), Array("か"))
|
||||
}
|
||||
|
||||
func testCustomTableLoadingWithCommentLines() throws {
|
||||
let url = FileManager.default.temporaryDirectory.appendingPathComponent("custom.tsv")
|
||||
try "a\tあ\n# here is comment\nka\tか\n".write(to: url, atomically: true, encoding: .utf8)
|
||||
let table = InputStyleManager.shared.table(for: .custom(url))
|
||||
XCTAssertEqual(table.toHiragana(currentText: [], added: "a"), Array("あ"))
|
||||
XCTAssertEqual(table.toHiragana(currentText: ["k"], added: "a"), Array("か"))
|
||||
}
|
||||
}
|
@ -3,24 +3,25 @@ import XCTest
|
||||
|
||||
final class Roman2KanaTests: XCTestCase {
|
||||
func testToHiragana() throws {
|
||||
let table = InputStyleManager.shared.table(for: .defaultRomanToKana)
|
||||
// xtsu -> っ
|
||||
XCTAssertEqual(Roman2Kana.toHiragana(currentText: Array(""), added: "x"), Array("x"))
|
||||
XCTAssertEqual(Roman2Kana.toHiragana(currentText: Array("x"), added: "t"), Array("xt"))
|
||||
XCTAssertEqual(Roman2Kana.toHiragana(currentText: Array("xt"), added: "s"), Array("xts"))
|
||||
XCTAssertEqual(Roman2Kana.toHiragana(currentText: Array("xts"), added: "u"), Array("っ"))
|
||||
XCTAssertEqual(table.toHiragana(currentText: Array(""), added: "x"), Array("x"))
|
||||
XCTAssertEqual(table.toHiragana(currentText: Array("x"), added: "t"), Array("xt"))
|
||||
XCTAssertEqual(table.toHiragana(currentText: Array("xt"), added: "s"), Array("xts"))
|
||||
XCTAssertEqual(table.toHiragana(currentText: Array("xts"), added: "u"), Array("っ"))
|
||||
|
||||
// kanto -> かんと
|
||||
XCTAssertEqual(Roman2Kana.toHiragana(currentText: Array(""), added: "k"), Array("k"))
|
||||
XCTAssertEqual(Roman2Kana.toHiragana(currentText: Array("k"), added: "a"), Array("か"))
|
||||
XCTAssertEqual(Roman2Kana.toHiragana(currentText: Array("か"), added: "n"), Array("かn"))
|
||||
XCTAssertEqual(Roman2Kana.toHiragana(currentText: Array("かn"), added: "t"), Array("かんt"))
|
||||
XCTAssertEqual(Roman2Kana.toHiragana(currentText: Array("かんt"), added: "o"), Array("かんと"))
|
||||
XCTAssertEqual(table.toHiragana(currentText: Array(""), added: "k"), Array("k"))
|
||||
XCTAssertEqual(table.toHiragana(currentText: Array("k"), added: "a"), Array("か"))
|
||||
XCTAssertEqual(table.toHiragana(currentText: Array("か"), added: "n"), Array("かn"))
|
||||
XCTAssertEqual(table.toHiragana(currentText: Array("かn"), added: "t"), Array("かんt"))
|
||||
XCTAssertEqual(table.toHiragana(currentText: Array("かんt"), added: "o"), Array("かんと"))
|
||||
|
||||
// zl -> →
|
||||
XCTAssertEqual(Roman2Kana.toHiragana(currentText: Array(""), added: "z"), Array("z"))
|
||||
XCTAssertEqual(Roman2Kana.toHiragana(currentText: Array("z"), added: "l"), Array("→"))
|
||||
XCTAssertEqual(table.toHiragana(currentText: Array(""), added: "z"), Array("z"))
|
||||
XCTAssertEqual(table.toHiragana(currentText: Array("z"), added: "l"), Array("→"))
|
||||
|
||||
// TT -> TT
|
||||
XCTAssertEqual(Roman2Kana.toHiragana(currentText: Array("T"), added: "T"), Array("TT"))
|
||||
XCTAssertEqual(table.toHiragana(currentText: Array("T"), added: "T"), Array("TT"))
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,29 @@ final class ConverterTests: XCTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
func testAzikFullConversion() async throws {
|
||||
for needTypoCorrection in [true, false] {
|
||||
do {
|
||||
let converter = await KanaKanjiConverter()
|
||||
var c = ComposingText()
|
||||
// : -> ー, sk -> しん, dq → だい, kf -> き, ds: です
|
||||
c.insertAtCursorPosition("azu:ki:haskzidqnokf:bo:doapurids", inputStyle: .mapped(id: .defaultAZIK))
|
||||
XCTAssertEqual(c.convertTarget, "あずーきーはしんじだいのきーぼーどあぷりです")
|
||||
let results = await converter.requestCandidates(c, options: requestOptions(needTypoCorrection: needTypoCorrection))
|
||||
XCTAssertEqual(results.mainResults.first?.text, "azooKeyは新時代のキーボードアプリです")
|
||||
}
|
||||
do {
|
||||
let converter = await KanaKanjiConverter()
|
||||
var c = ComposingText()
|
||||
// yp -> よう, xp -> しょう, kf -> き, kr -> から, kyh -> きゅう, rk -> りん, kd -> けん, pp -> ぽう, : -> ー, kw -> けい, gr -> がら, ; -> っ, kp -> こう, dq -> だい, sz -> さん, kk -> きん, tq -> たい, zq -> ざい, tw -> てい
|
||||
c.insertAtCursorPosition("ypxpkfkrtenisusuieiyakyhxprkzikdppnadosamazamanasupo:tuwokwkdsinagrsodatixpga;kpzidqharoszzerusukkkpnitqzqsiteorigoruhuyatenisuwonara;twta", inputStyle: .mapped(id: .defaultAZIK))
|
||||
XCTAssertEqual(c.convertTarget, "ようしょうきからてにすすいえいやきゅうしょうりんじけんぽうなどさまざまなすぽーつをけいけんしながらそだちしょうがっこうじだいはろさんぜるすきんこうにたいざいしておりごるふやてにすをならっていた")
|
||||
let results = await converter.requestCandidates(c, options: requestOptions(needTypoCorrection: needTypoCorrection))
|
||||
XCTAssertEqual(results.mainResults.first?.text, "幼少期からテニス水泳野球少林寺拳法など様々なスポーツを経験しながら育ち小学校時代はロサンゼルス近郊に滞在しておりゴルフやテニスを習っていた")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1文字ずつ変換する
|
||||
// memo: 内部実装としては別のモジュールが呼ばれるのだが、それをテストする方法があまりないかもしれない
|
||||
func testGradualConversion() async throws {
|
||||
|
@ -331,7 +331,7 @@ final class DicdataStoreTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testPossibleNexts() throws {
|
||||
let possibleNexts = DicdataStore.possibleNexts
|
||||
let possibleNexts = InputStyleManager.shared.table(for: .defaultRomanToKana).possibleNexts
|
||||
XCTAssertEqual(Set(possibleNexts["f", default: []]).symmetricDifference(["ファ", "フィ", "フ", "フェ", "フォ", "フャ", "フュ", "フョ", "フゥ", "ッf"]), [])
|
||||
XCTAssertEqual(Set(possibleNexts["xy", default: []]).symmetricDifference(["ャ", "ョ", "ュ"]), [])
|
||||
XCTAssertEqual(possibleNexts["", default: []], [])
|
||||
|
@ -24,18 +24,6 @@ final class CharacterUtilsTests: XCTestCase {
|
||||
XCTAssertFalse(CharacterUtils.isKogana("!"))
|
||||
}
|
||||
|
||||
func testIsRomanLetter() throws {
|
||||
XCTAssertTrue(CharacterUtils.isRomanLetter("a"))
|
||||
XCTAssertTrue(CharacterUtils.isRomanLetter("A"))
|
||||
XCTAssertTrue(CharacterUtils.isRomanLetter("b"))
|
||||
|
||||
XCTAssertFalse(CharacterUtils.isRomanLetter("ぁ"))
|
||||
XCTAssertFalse(CharacterUtils.isRomanLetter("'"))
|
||||
XCTAssertFalse(CharacterUtils.isRomanLetter("あ"))
|
||||
XCTAssertFalse(CharacterUtils.isRomanLetter("カ"))
|
||||
XCTAssertFalse(CharacterUtils.isRomanLetter("!"))
|
||||
}
|
||||
|
||||
func testIsDakuten() throws {
|
||||
XCTAssertTrue(CharacterUtils.isDakuten("が"))
|
||||
XCTAssertTrue(CharacterUtils.isDakuten("ば"))
|
||||
|
Reference in New Issue
Block a user