Add Arabic pronunciation processing
This commit is contained in:
+104
@@ -0,0 +1,104 @@
|
||||
import Foundation
|
||||
|
||||
public struct ArabicPronunciationProcessor: Sendable {
|
||||
public init() {}
|
||||
|
||||
public func process(
|
||||
lyrics: String,
|
||||
settings: ArabicPronunciationSettings
|
||||
) -> ArabicPronunciationProcessingResult {
|
||||
guard settings.isEnabled else {
|
||||
return ArabicPronunciationProcessingResult(
|
||||
text: lyrics,
|
||||
notes: [.processingDisabled]
|
||||
)
|
||||
}
|
||||
|
||||
var processedText = lyrics.precomposedStringWithCanonicalMapping
|
||||
var notes = processingNotes(for: processedText, settings: settings)
|
||||
|
||||
if settings.tanweenPolicy == .removeWhenUnwanted {
|
||||
processedText = removeTanween(from: processedText)
|
||||
}
|
||||
|
||||
if settings.tanweenPolicy == .addWhenPronunciationRequires,
|
||||
containsArabicLetter(in: processedText),
|
||||
!containsTanween(in: processedText) {
|
||||
notes.append(.tanweenAdditionNeedsReview)
|
||||
}
|
||||
|
||||
return ArabicPronunciationProcessingResult(
|
||||
text: processedText,
|
||||
notes: notes
|
||||
)
|
||||
}
|
||||
|
||||
private func processingNotes(
|
||||
for text: String,
|
||||
settings: ArabicPronunciationSettings
|
||||
) -> [ArabicPronunciationProcessingNote] {
|
||||
guard containsArabicLetter(in: text) else {
|
||||
return []
|
||||
}
|
||||
|
||||
switch settings.diacritizationPolicy {
|
||||
case .unspecified:
|
||||
return [.diacritizationPolicyUnspecified]
|
||||
case .pronunciationTargeted where !containsDiacritics(in: text):
|
||||
return [.pronunciationTargetedDiacriticsNeedReview]
|
||||
case .fullTashkeel where !containsDiacritics(in: text):
|
||||
return [.fullTashkeelNeedsReview]
|
||||
default:
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
private func removeTanween(from text: String) -> String {
|
||||
String(text.unicodeScalars.filter { scalar in
|
||||
!isTanween(scalar)
|
||||
})
|
||||
}
|
||||
|
||||
private func containsArabicLetter(in text: String) -> Bool {
|
||||
text.unicodeScalars.contains { scalar in
|
||||
let isArabicBlock = (0x0600...0x06FF).contains(scalar.value)
|
||||
|| (0x0750...0x077F).contains(scalar.value)
|
||||
|| (0x08A0...0x08FF).contains(scalar.value)
|
||||
return isArabicBlock && CharacterSet.letters.contains(scalar)
|
||||
}
|
||||
}
|
||||
|
||||
private func containsDiacritics(in text: String) -> Bool {
|
||||
text.unicodeScalars.contains(where: isArabicDiacritic)
|
||||
}
|
||||
|
||||
private func containsTanween(in text: String) -> Bool {
|
||||
text.unicodeScalars.contains(where: isTanween)
|
||||
}
|
||||
|
||||
private func isArabicDiacritic(_ scalar: Unicode.Scalar) -> Bool {
|
||||
(0x064B...0x065F).contains(scalar.value) || scalar.value == 0x0670
|
||||
}
|
||||
|
||||
private func isTanween(_ scalar: Unicode.Scalar) -> Bool {
|
||||
(0x064B...0x064D).contains(scalar.value)
|
||||
}
|
||||
}
|
||||
|
||||
public struct ArabicPronunciationProcessingResult: Equatable, Sendable {
|
||||
public var text: String
|
||||
public var notes: [ArabicPronunciationProcessingNote]
|
||||
|
||||
public init(text: String, notes: [ArabicPronunciationProcessingNote] = []) {
|
||||
self.text = text
|
||||
self.notes = notes
|
||||
}
|
||||
}
|
||||
|
||||
public enum ArabicPronunciationProcessingNote: Equatable, Sendable {
|
||||
case processingDisabled
|
||||
case diacritizationPolicyUnspecified
|
||||
case pronunciationTargetedDiacriticsNeedReview
|
||||
case fullTashkeelNeedsReview
|
||||
case tanweenAdditionNeedsReview
|
||||
}
|
||||
Reference in New Issue
Block a user