|
1 | | -public struct KorToBraille { |
2 | | - public private(set) var text = "Hello, World!" |
| 1 | +import Foundation |
3 | 2 |
|
4 | | - public init() { |
| 3 | +public class KorToBraille { |
| 4 | + |
| 5 | + public init() {} |
| 6 | + |
| 7 | +// static var cnt = 0 // 지금까지 나온 음절의 수 |
| 8 | +// static var wordCnt = 0 // 지금까지 나온 단어의 수 |
| 9 | + // UTF-8 기준 |
| 10 | + static let INDEX_HANGUL_START:UInt32 = 44032 // "가" |
| 11 | + static let INDEX_HANGUL_END:UInt32 = 55199 // "힣" |
| 12 | + |
| 13 | + static let CYCLE_CHO :UInt32 = 588 |
| 14 | + static let CYCLE_JUNG :UInt32 = 28 |
| 15 | + |
| 16 | + static let CHO = [ |
| 17 | + "ㄱ","ㄲ","ㄴ","ㄷ","ㄸ","ㄹ","ㅁ","ㅂ","ㅃ","ㅅ", |
| 18 | + "ㅆ","ㅇ","ㅈ","ㅉ","ㅊ","ㅋ","ㅌ","ㅍ","ㅎ" |
| 19 | + ] |
| 20 | + |
| 21 | + static let JUNG = [ |
| 22 | + "ㅏ", "ㅐ", "ㅑ", "ㅒ", "ㅓ", "ㅔ","ㅕ", "ㅖ", "ㅗ", "ㅘ", |
| 23 | + "ㅙ", "ㅚ","ㅛ", "ㅜ", "ㅝ", "ㅞ", "ㅟ", "ㅠ", "ㅡ", "ㅢ", |
| 24 | + "ㅣ" |
| 25 | + ] |
| 26 | + |
| 27 | + static let JONG = [ |
| 28 | + "","ㄱ","ㄲ","ㄳ","ㄴ","ㄵ","ㄶ","ㄷ","ㄹ","ㄺ", |
| 29 | + "ㄻ","ㄼ","ㄽ","ㄾ","ㄿ","ㅀ","ㅁ","ㅂ","ㅄ","ㅅ", |
| 30 | + "ㅆ","ㅇ","ㅈ","ㅊ","ㅋ","ㅌ","ㅍ","ㅎ" |
| 31 | + ] |
| 32 | + |
| 33 | + static let JONG_DOUBLE = [ |
| 34 | + "ㄲ":"ㄱㄱ","ㄳ":"ㄱㅅ","ㄵ":"ㄴㅈ","ㄶ":"ㄴㅎ","ㄺ":"ㄹㄱ","ㄻ":"ㄹㅁ", |
| 35 | + "ㄼ":"ㄹㅂ","ㄽ":"ㄹㅅ","ㄾ":"ㄹㅌ","ㄿ":"ㄹㅍ","ㅀ":"ㄹㅎ", |
| 36 | + "ㅄ":"ㅂㅅ" |
| 37 | + ] |
| 38 | + |
| 39 | + /* [약어 번역] |
| 40 | + 제18항: 다음이 단어들은 약어로 적어 나타낸다. |
| 41 | + [붙임] 위에 제시된 말들의 뒤에 다른 음절이 붙어 쓰일 때에도 약어를 사용하여 적는다. ex. 그래서인지, 그러면서 |
| 42 | + [다만] 위에 제시된 말들의 앞에 다른 음절이 붙어 쓰일 때에는 약어를 사용하여 적지 않는다. ex. 쭈그리고, 찡그리고, 오그리고 |
| 43 | + */ |
| 44 | + private class func korabbToBraille(_ input: String) -> String { |
| 45 | + |
| 46 | + var result = input |
| 47 | + |
| 48 | + for (key, value) in kor_abb{ |
| 49 | + if input.contains(key){ |
| 50 | + if let index = input.range(of: key){ |
| 51 | + var s = 1 |
| 52 | + s = input.distance(from: input.startIndex, to: index.lowerBound) |
| 53 | + if s == 0{ // [다만] -> 맨 앞에 오는 경우인지 확인 |
| 54 | + result = result.replace(target: key, withString: value) |
| 55 | + } |
| 56 | + } |
| 57 | + |
| 58 | + } |
| 59 | + } |
| 60 | + |
| 61 | + return result |
| 62 | + } |
| 63 | + |
| 64 | + |
| 65 | + // [최종 번역] 주어진 문장을 단어로 decompose -> korWordToBraille로 각각 점자 단어로 변경 -> 점자 단어 compose (result) |
| 66 | + public class func korTranslate(_ input: String) -> String{ |
| 67 | + |
| 68 | + var result = "" |
| 69 | + let components = input.components(separatedBy: " ") // 띄어쓰기 단위로 끊음 |
| 70 | + |
| 71 | + for word in components{ |
| 72 | + var word_translatedNumber = translateNumber(text: word) // (1) 숫자 번역 |
| 73 | + word_translatedNumber = translatePunc(text: word_translatedNumber) //(2) 문장부호 번역 |
| 74 | + result += korWordToBraille(word_translatedNumber) // (3) 한글 번역 |
| 75 | + |
| 76 | + result += "⠀" |
| 77 | + |
| 78 | + // 모든 flag 초기화 |
| 79 | + flag_10 = false |
| 80 | + flag_11 = false |
| 81 | + flag_17 = false |
| 82 | + isdigit_flag = false |
| 83 | + } |
| 84 | + |
| 85 | + return result |
| 86 | + } |
| 87 | + |
| 88 | + |
| 89 | + // [단어 번역] 주어진 "단어"를 자모음으로 분해해서 번역된 점자로 리턴하는 함수 |
| 90 | + private class func korWordToBraille(_ input: String) -> String { |
| 91 | + |
| 92 | + var wordToTranslate = "" // 번역할 단어 |
| 93 | + |
| 94 | + var jamo = "" // 분해된 문장 |
| 95 | + var braillejamo = "" // 분해된 점자 문장 |
| 96 | + //var jamoCursor = 0 // 현재 문장 커서 -> 위치 파악을 위한 index 역할 |
| 97 | + //var brailleCursor = 0 // 현재 점자 문장 커서 -> 위치 파악을 위한 index 역할 |
| 98 | + |
| 99 | + wordToTranslate = korabbToBraille(input) // 약어 처리 |
| 100 | + |
| 101 | + for scalar in wordToTranslate.unicodeScalars{ |
| 102 | + jamo += getJamoFromOneSyllable(scalar) ?? "" // 자모음 분해 |
| 103 | + braillejamo += getBrailleFromJamo(scalar) ?? "" // 점자 번역 |
| 104 | + } |
| 105 | + |
| 106 | + return braillejamo |
| 107 | + } |
| 108 | + |
| 109 | + // [점자 번역] 자모음으로 분해한 음절을 "점자"로 번역하여 리턴하는 함수 (한 음절씩 번역 처리(초성+중성+종성)) |
| 110 | + private class func getBrailleFromJamo(_ n: UnicodeScalar) -> String?{ |
| 111 | + if CharacterSet(charactersIn: ("가".unicodeScalars.first!)...("힣".unicodeScalars.first!)).contains(n){ |
| 112 | + let index = n.value - INDEX_HANGUL_START |
| 113 | + |
| 114 | + // [초성] |
| 115 | + var cho = CHO[Int(index / CYCLE_CHO)] |
| 116 | + |
| 117 | + // 제2항: ‘ᄋ’이 첫소리 자리에 쓰일 때에는 이를 표기하지 않는다. |
| 118 | + if cho == "ㅇ"{ |
| 119 | + cho = "" |
| 120 | + } |
| 121 | + |
| 122 | + var braille_cho = kor_cho["\(cho)"]! // 초성 점자 |
| 123 | + |
| 124 | + |
| 125 | + // [중성] |
| 126 | + let jung = JUNG[Int((index % CYCLE_CHO) / CYCLE_JUNG)] |
| 127 | + |
| 128 | + var braille_jung = kor_jung["\(jung)"]! // 중성 점자 |
| 129 | + |
| 130 | + |
| 131 | + if cho == "" && jung == "ㅖ" && flag_10 { // 제10항: 모음자에 '예'가 이어 나올 때에는 그 사이에 붙임표(⠤)를 적어 나타낸다. |
| 132 | + braille_jung.insert("⠤", at: braille_jung.startIndex) |
| 133 | + flag_10 = false |
| 134 | + }else if cho == "" && jung == "ㅐ" && flag_11{ // 제11항: 'ㅑ,ㅘ,ㅜ,ㅝ'에 '애'가 이어 나올 때에는 그 사이에 붙임표(⠤)를 적어 나타낸다. |
| 135 | + braille_jung.insert("⠤", at: braille_jung.startIndex) |
| 136 | + flag_11 = false |
| 137 | + }else if cho == "" && flag_17{ // 제17항: 한 어절 안에서 'ㅏ'를 생략한 약자에 받침 글자가 없고 다음 음절이 모음으로 시작될 때에는 'ㅏ'를 생략하지 않는다. |
| 138 | + braille_cho.insert("⠣", at: braille_cho.startIndex) |
| 139 | + flag_17 = false |
| 140 | + } |
| 141 | + |
| 142 | + // 제12항: 다음 글자가 포함된 글자들은 아래 표에 제시한 약자 표기를 이용하여 적는 것을 표준으로 삼는다. |
| 143 | + if jung == "ㅏ" { |
| 144 | + if cho == "ㄱ" { |
| 145 | + braille_cho = "" |
| 146 | + braille_jung = "⠫" |
| 147 | + }else if cho == "ㅅ" { |
| 148 | + braille_cho = "" |
| 149 | + braille_jung = "⠇" |
| 150 | + }else if cho == "ㄴ" || cho == "ㄷ" || cho == "ㅁ" || cho == "ㅂ" || cho == "ㅈ" || cho == "ㅋ" || cho == "ㅌ" || cho == "ㅍ" || cho == "ㅎ"{ |
| 151 | + braille_cho = "" // '나,다,마,바,자,카,타,파,하'는 모음 'ㅏ'를 생략하고 첫소리 글자로 약자 표기한다. |
| 152 | + braille_jung = kor_cho["\(cho)"]! |
| 153 | + flag_17 = true |
| 154 | + }else if cho == "ㄸ"{ |
| 155 | + braille_cho = "" |
| 156 | + braille_jung = "⠠⠊" |
| 157 | + flag_17 = true |
| 158 | + }else if cho == "ㅃ"{ |
| 159 | + braille_cho = "" |
| 160 | + braille_jung = "⠠⠘" |
| 161 | + flag_17 = true |
| 162 | + }else if cho == "ㅉ"{ |
| 163 | + braille_cho = "" |
| 164 | + braille_jung = "⠠⠨" |
| 165 | + flag_17 = true |
| 166 | + }else if cho == "ㄲ"{ // 제14항 '까,싸,껏'은 각각 '가,사,것'의 약자 표기에 된소리 표를 덧붙여 적는다. |
| 167 | + braille_cho = "" |
| 168 | + braille_jung = "⠠⠫" |
| 169 | + }else if cho == "ㅆ"{ |
| 170 | + braille_cho = "" |
| 171 | + braille_jung = "⠠⠇" |
| 172 | + } |
| 173 | + } |
| 174 | + |
| 175 | + |
| 176 | + // [종성] * 종성은 없는 경우도 있음 |
| 177 | + var jong = JONG[Int(index % CYCLE_JUNG)] |
| 178 | + guard var braille_jong = kor_jong["\(jong)"] else { |
| 179 | + flag_10 = true // 종성 없음(모음자) |
| 180 | + if jung == "ㅑ" || jung == "ㅘ" || jung == "ㅜ" || jung == "ㅝ"{ |
| 181 | + flag_11 = true |
| 182 | + } |
| 183 | + |
| 184 | + return braille_cho + braille_jung |
| 185 | + } // 종성 점자 |
| 186 | + |
| 187 | + |
| 188 | + // 겹받침 처리를 위해 first와 second로 나누었음 |
| 189 | + var firstjong: Character = " " |
| 190 | + var secondjong: Character = " " |
| 191 | + |
| 192 | + |
| 193 | + // 종성이 double(ex. ㄲ, ㄹㄱ ..)일 때 |
| 194 | + if let disassembledJong = JONG_DOUBLE[jong] { |
| 195 | + jong = disassembledJong |
| 196 | + firstjong = jong[jong.startIndex] |
| 197 | + secondjong = jong[jong.index(jong.endIndex, offsetBy:-1)] |
| 198 | + } |
| 199 | + |
| 200 | + // 제12항: 다음 글자가 포함된 글자들은 아래 표에 제시한 약자 표기를 이용하여 적는 것을 표준으로 삼는다. |
| 201 | + // 제15항: 다음과 같이 글자 속에 모음으로 시작하는 약자가 포함되어 있을 때에는 해당 약자를 이용하여 적는다. |
| 202 | + if jung == "ㅓ"{ |
| 203 | + // 글자 속에 "ㅓㄱ"이 포함된 경우 |
| 204 | + if jong == "ㄱ"{ |
| 205 | + braille_jung = "" |
| 206 | + braille_jong = "⠹" |
| 207 | + }else if firstjong == "ㄱ"{ |
| 208 | + braille_jung = "" |
| 209 | + braille_jong = "⠹" + kor_jong["\(secondjong)"]! |
| 210 | + }else if jong == "ㄴ"{ // 글자 속에 "ㅓㄴ"이 포함된 경우 |
| 211 | + braille_jung = "" |
| 212 | + braille_jong = "⠾" |
| 213 | + }else if firstjong == "ㄴ"{ |
| 214 | + braille_jung = "" |
| 215 | + braille_jong = "⠾" + kor_jong["\(secondjong)"]! |
| 216 | + }else if jong == "ㄹ"{ // 글자 속에 "ㅓㄹ"이 포함된 경우 |
| 217 | + braille_jung = "" |
| 218 | + braille_jong = "⠞" |
| 219 | + }else if firstjong == "ㄹ"{ |
| 220 | + braille_jung = "" |
| 221 | + braille_jong = "⠞" + kor_jong["\(secondjong)"]! |
| 222 | + } // 한글점자규정 제16항: '성,썽,정,쩡,청'은 'ㅅ,ㅆ,ㅈ,ㅉ,ㅊ' 다음에 'ㅕㅇ'의 약자('⠻')를 적어 나타낸다. |
| 223 | + else if jong == "ㅇ" && (cho == "ㅅ" || cho == "ㅆ" || cho == "ㅈ" || cho == "ㅉ" || cho == "ㅊ"){ |
| 224 | + braille_jung = "" |
| 225 | + braille_jong = "⠻" |
| 226 | + } |
| 227 | + }else if jung == "ㅕ"{ |
| 228 | + if jong == "ㄴ"{ // 글자 속에 "ㅕㄴ"이 포함된 경우 |
| 229 | + braille_jung = "" |
| 230 | + braille_jong = "⠡" |
| 231 | + }else if firstjong == "ㄴ"{ |
| 232 | + braille_jung = "" |
| 233 | + braille_jong = "⠡" + kor_jong["\(secondjong)"]! |
| 234 | + }else if jong == "ㄹ"{ // 글자 속에 "ㅕㄹ"이 포함된 경우 |
| 235 | + braille_jung = "" |
| 236 | + braille_jong = "⠳" |
| 237 | + }else if firstjong == "ㄹ"{ |
| 238 | + braille_jung = "" |
| 239 | + braille_jong = "⠳" + kor_jong["\(secondjong)"]! |
| 240 | + }else if jong == "ㅇ"{ // 글자 속에 "ㅕㅇ"이 포함된 경우 |
| 241 | + braille_jung = "" |
| 242 | + braille_jong = "⠻" |
| 243 | + } |
| 244 | + }else if jung == "ㅗ"{ |
| 245 | + if jong == "ㄱ"{ // 글자 속에 "ㅗㄱ"이 포함된 경우 |
| 246 | + braille_jung = "" |
| 247 | + braille_jong = "⠭" |
| 248 | + }else if firstjong == "ㄱ"{ |
| 249 | + braille_jung = "" |
| 250 | + braille_jong = "⠭" + kor_jong["\(secondjong)"]! |
| 251 | + }else if jong == "ㄴ"{ // 글자 속에 "ㅗㄴ"이 포함된 경우 |
| 252 | + braille_jung = "" |
| 253 | + braille_jong = "⠷" |
| 254 | + }else if firstjong == "ㄴ"{ |
| 255 | + braille_jung = "" |
| 256 | + braille_jong = "⠷" + kor_jong["\(secondjong)"]! |
| 257 | + }else if jong == "ㅇ"{ // 글자 속에 "ㅗㅇ"이 포함된 경우 |
| 258 | + braille_jung = "" |
| 259 | + braille_jong = "⠿" |
| 260 | + } |
| 261 | + }else if jung == "ㅜ"{ |
| 262 | + if jong == "ㄴ"{ // 글자 속에 "ㅜㄴ"이 포함된 경우 |
| 263 | + braille_jung = "" |
| 264 | + braille_jong = "⠛" |
| 265 | + }else if firstjong == "ㄴ"{ |
| 266 | + braille_jung = "" |
| 267 | + braille_jong = "⠛" + kor_jong["\(secondjong)"]! |
| 268 | + }else if jong == "ㄹ"{ // 글자 속에 "ㅜㄹ"이 포함된 경우 |
| 269 | + braille_jung = "" |
| 270 | + braille_jong = "⠯" |
| 271 | + }else if firstjong == "ㄹ"{ |
| 272 | + braille_jung = "" |
| 273 | + braille_jong = "⠯" + kor_jong["\(secondjong)"]! |
| 274 | + } |
| 275 | + }else if jung == "ㅡ"{ |
| 276 | + if jong == "ㄴ"{ // 글자 속에 "ㅡㄴ"이 포함된 경우 |
| 277 | + braille_jung = "" |
| 278 | + braille_jong = "⠵" |
| 279 | + }else if firstjong == "ㄴ"{ |
| 280 | + braille_jung = "" |
| 281 | + braille_jong = "⠵" + kor_jong["\(secondjong)"]! |
| 282 | + }else if jong == "ㄹ"{ // 글자 속에 "ㅡㄹ"이 포함된 경우 |
| 283 | + braille_jung = "" |
| 284 | + braille_jong = "⠮" |
| 285 | + }else if firstjong == "ㄹ"{ |
| 286 | + braille_jung = "" |
| 287 | + braille_jong = "⠮" + kor_jong["\(secondjong)"]! |
| 288 | + } |
| 289 | + }else if jung == "ㅣ"{ |
| 290 | + if jong == "ㄴ"{ // 글자 속에 "ㅣㄴ"이 포함된 경우 |
| 291 | + braille_jung = "" |
| 292 | + braille_jong = "⠟" |
| 293 | + }else if firstjong == "ㄴ"{ |
| 294 | + braille_jung = "" |
| 295 | + braille_jong = "⠟" + kor_jong["\(secondjong)"]! |
| 296 | + } |
| 297 | + } |
| 298 | + |
| 299 | + // 한글점자규정 제12항: 것은 약자("⠸⠎")를 사용한다. |
| 300 | + if n == "것"{ |
| 301 | + braille_cho = "" |
| 302 | + braille_jong = "" |
| 303 | + braille_jung = "⠸⠎" |
| 304 | + }else if n == "껏"{ // 한글점자규정 제14항: '까,싸,껏'은 각각 '가,사,것'의 약자 표기에 된소리 표를 덧붙여 적는다. |
| 305 | + braille_cho = "" |
| 306 | + braille_jong = "" |
| 307 | + braille_jung = "⠠⠸⠎" |
| 308 | + }else if n == "팠"{ // 한글점자규정 제17항 [붙임]: '팠'을 적을 때에는 'ㅏ'를 생략하지 않고 적는다. |
| 309 | + braille_cho = "" |
| 310 | + braille_jong = "" |
| 311 | + braille_jung = "⠙⠣⠌" |
| 312 | + } |
| 313 | + |
| 314 | + flag_17 = false |
| 315 | + |
| 316 | + |
| 317 | + return braille_cho + braille_jung + braille_jong |
| 318 | + |
| 319 | + }else{ |
| 320 | + return String(UnicodeScalar(n)) |
| 321 | + } |
| 322 | + } |
| 323 | + |
| 324 | + // [한글 분해] 자모음으로 분해해서 리턴하는 함수 (한글로 규칙을 파악해야할 때 사용) |
| 325 | + private class func getJamoFromOneSyllable(_ n: UnicodeScalar) -> String?{ |
| 326 | + if CharacterSet(charactersIn: ("가".unicodeScalars.first!)...("힣".unicodeScalars.first!)).contains(n){ |
| 327 | + let index = n.value - INDEX_HANGUL_START |
| 328 | + |
| 329 | + let cho = CHO[Int(index / CYCLE_CHO)] |
| 330 | + |
| 331 | + let jung = JUNG[Int((index % CYCLE_CHO) / CYCLE_JUNG)] |
| 332 | + |
| 333 | + // 종성은 없는 경우도 있음 |
| 334 | + var jong = JONG[Int(index % CYCLE_JUNG)] |
| 335 | + if let disassembledJong = JONG_DOUBLE[jong] { |
| 336 | + jong = disassembledJong |
| 337 | + } |
| 338 | + |
| 339 | + return cho + jung + jong |
| 340 | + }else{ |
| 341 | + return String(UnicodeScalar(n)) |
| 342 | + } |
5 | 343 | } |
6 | 344 | } |
0 commit comments