100 lines
2.8 KiB
Swift
100 lines
2.8 KiB
Swift
import Foundation
|
|
import NaturalLanguage
|
|
|
|
protocol LanguageDetecting: Sendable {
|
|
func detect(_ text: String) -> TranslatorLanguage?
|
|
}
|
|
|
|
struct LanguageDetector: LanguageDetecting {
|
|
func detect(_ text: String) -> TranslatorLanguage? {
|
|
let trimmedText = text.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
|
|
guard !trimmedText.isEmpty else {
|
|
return nil
|
|
}
|
|
|
|
let recognizer = NLLanguageRecognizer()
|
|
recognizer.processString(trimmedText)
|
|
|
|
if let language = recognizer.dominantLanguage,
|
|
let translatorLanguage = Self.map(language) {
|
|
return translatorLanguage
|
|
}
|
|
|
|
return Self.detectByScript(trimmedText)
|
|
}
|
|
|
|
private static func map(_ language: NLLanguage) -> TranslatorLanguage? {
|
|
switch language {
|
|
case .simplifiedChinese:
|
|
return .simplifiedChinese
|
|
case .traditionalChinese:
|
|
return .traditionalChinese
|
|
case .english:
|
|
return .english
|
|
case .japanese:
|
|
return .japanese
|
|
case .korean:
|
|
return .korean
|
|
case .french:
|
|
return .french
|
|
case .german:
|
|
return .german
|
|
case .spanish:
|
|
return .spanish
|
|
case .portuguese:
|
|
return .portuguese
|
|
case .italian:
|
|
return .italian
|
|
case .russian:
|
|
return .russian
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
private static func detectByScript(_ text: String) -> TranslatorLanguage? {
|
|
var hasLatinLetter = false
|
|
|
|
for scalar in text.unicodeScalars {
|
|
if Self.isJapaneseScript(scalar) {
|
|
return .japanese
|
|
}
|
|
|
|
if Self.isKoreanScript(scalar) {
|
|
return .korean
|
|
}
|
|
|
|
if Self.isCJKScript(scalar) {
|
|
return .simplifiedChinese
|
|
}
|
|
|
|
if Self.isLatinLetter(scalar) {
|
|
hasLatinLetter = true
|
|
}
|
|
}
|
|
|
|
return hasLatinLetter ? .english : nil
|
|
}
|
|
|
|
private static func isJapaneseScript(_ scalar: UnicodeScalar) -> Bool {
|
|
(0x3040...0x309F).contains(Int(scalar.value)) ||
|
|
(0x30A0...0x30FF).contains(Int(scalar.value))
|
|
}
|
|
|
|
private static func isKoreanScript(_ scalar: UnicodeScalar) -> Bool {
|
|
(0xAC00...0xD7AF).contains(Int(scalar.value)) ||
|
|
(0x1100...0x11FF).contains(Int(scalar.value)) ||
|
|
(0x3130...0x318F).contains(Int(scalar.value))
|
|
}
|
|
|
|
private static func isCJKScript(_ scalar: UnicodeScalar) -> Bool {
|
|
(0x4E00...0x9FFF).contains(Int(scalar.value))
|
|
}
|
|
|
|
private static func isLatinLetter(_ scalar: UnicodeScalar) -> Bool {
|
|
(0x0041...0x005A).contains(Int(scalar.value)) ||
|
|
(0x0061...0x007A).contains(Int(scalar.value))
|
|
}
|
|
}
|