기존 번역 기능
애플은 사용자가 번역 기능을 지원하는 머신 러닝의 혜택을 누리는 등 언어 장벽을 없애기 위해서 많은 시도를 하고 있습니다.
기존에 이런 기능들은 다양한 언어들을 번역할 수 있도록 도와주며 여러 곳에서 제공하고 있습니다.
- 번역 앱에서 Text to Text 와 Image to Text 로 번역하는 기능
- 메시지에서 번역 sheet 를 제공
새로운 번역 기능 제공
애플은 WWDC24 에서 번역을 지원하는 머신러닝 모델을 사용할 수 있는 API 를 공개하였습니다.
모든 번역 API 는 iOS, iPadOS, macOS 에서 지원됩니다.
예를들어 여러 나라의 댓글이 달렸을 때 해당 언어들을 각각 번역하는데 있어서 어려움을 겪을 수 있습니다. 이때 새로운 Translation API 를 사용하게 된다면 쉽게 번역 기능을 사용할 수 있게 됩니다.
댓글을 번역하기 위해 기존에 제공하던 기능으로 번역하는 방법들은 다음과 같았습니다.
- 간단한 translation overlay 제공
- 텍스트 translation
- 프로그래밍 방식으로 다국어 지원
간단한 translation overrlay
가장 간단한 번역 기능을 앱에 적용하는 방법으로는 .translationPresentation() 메서드를 사용하는것 입니다.
@State private var pressed = false
var body: some View {
VStack {
Button {
pressed.toggle()
} label: {
Text("버튼")
}
.translationPresentation(isPresented: $pressed, text: "안녕하세요")
}
.padding()
}
위와 같은 번역 기능은 매우 간단하지만 다음과 같은 분명 제약이 존재합니다.
- 여러 글들을 한번에 번역해야할 때
- 변화하는 글들을 번역해야할 때
유연한 translation API
이런 제약 사항들을 해결하기 위해서 번역문들을 inline 으로 표시할 수도 있습니다.
이런 문제들을 해결할 수 있는 translation API 의 핵심은 TranslationSession 클래스입니다.
TranslationSession
TranslationSession 을 사용하여 한 번에 여러 문자열을 번역하고 비동기적으로 번역 결과를 받아올 수 있습니다.
let session: TranslationSession
let inputStrings: [String] = []
let requests: [TranslationSession.Request] = inputStrings.map { .init(sourceText: $0) }
for response in try? wait session.translations(from: requests) {
handle(response: response)
}
간단한 사용 방법을 이제 알아보겠습니다.
상단과 같이 translationSession 을 인스턴스로 만들어 직접적으로 사용하지 않습니다. 대신 .translationTask 를 View 에서 사용하면 클로저를 호출하여 TranslationSession 을 사용할 수 있게 제공해줍니다.
import SwiftUI
import Translation
struct ContentView: View {
var sourceText: String
@State private var targetText: String?
var body: some View {
VStack {
Text(targetText ?? sourceText)
.translationTask { session in
do {
let response = try await session.translate(sourceText)
targetText = response.targetText
} catch {
}
}
}
.padding()
}
}
한개의 String 번역하기 with. TranslationSession.Configuration
TranslationSession의 Configuration 을 사용하면 Configuration이 변경될 때마다 변역을 새로합니다
만약 번역에 더 많은 제어권을 원한다면 TranslationSession.Configuration 을 사용합니다. configuartion 을 파라미터에 제공하게 된다면 nil 이 아닌 configuration 이 변경될때마다 클로저(번역)가 실행됩니다.
- 초기에 번역을 시작하기 위해서는 nil 이 아닌 값으로 구성합니다. 만약 번역을 다시 추가적으로 해야한다면 configuration 을 다시 변경해야 합니다.
- configuration 을 사용하여 출발어나 도착어를 변경할 수 있습니다.
- invalidate() 메서드를 실행하면 현재 기존의 configuration 으로 새로운 번역을 수행합니다.
아래 코드를 간단히 설명하면 TextField 에 원하는 값을 타이핑하고 이후 버튼을 누르면 해당 TextField 의 String 이 영어 -> 한글로 번역되는 기능을 수행하는 코드입니다.
struct ContentView: View {
@State private var sourceText: String = ""
@State private var targetText: String?
@State private var configuration: TranslationSession.Configuration?
var body: some View {
VStack {
TextField(text: $sourceText) { Text("textfield") }
Text(targetText ?? sourceText)
.translationTask(configuration) { session in
do {
let response = try await session.translate(sourceText)
targetText = response.targetText
} catch {
print(error.localizedDescription)
}
}
Button {
if configuration == nil {
configuration = .init(source: Locale.Language(identifier: "en"),
target: Locale.Language(identifier: "ko"))
} else {
configuration?.invalidate()
}
} label: {
Text("Button")
}
}
.padding()
}
}
만약 처음 이 기능을 사용하게 된다면 해당 설정한 언어를 번역하기 위해 언어 소스를 다운받아야합니다.
다운로드는 사용자가 sheet을 닫거나 앱을 종료하게 되더라도 백그라운드에서 다운이 진행됩니다.
여러개의 String 번역하기 with. TranslationSession.Configuration
이때 사용시 중요한 점은 Session 의 출발어 source 는 nil 이여야 합니다.
func translate(using session: TranslationSession) async {
do {
let englishRequests: [TranslationSession.Request] = [TranslationSession.Request(sourceText: "no thanks")]
let germanRequests: [TranslationSession.Request] = [TranslationSession.Request(sourceText: "Guten Abend")]
for try await resposne in session.translate(batch: englishRequests) {
print(resposne.targetText)
}
for try await resposne in session.translate(batch: germanRequests) {
print(resposne.targetText)
}
} catch {
print(error.localizedDescription)
}
}
여러 언어 한번에 번역할 때 주의할 점
- Request 를 만들 때 여러 언어를 한번에 요청하게 된다면 원치않는 결과를 받을 수 있게 됩니다.
- 대신 각 언어를 번역하는 호출을 별도로 작성해야 합니다.
- 여러 언어를 한번에 번역 요청하면 안됩니다.
- 출발지 언어와 목적지 언어가 동일하면 안됩니다.
에러 발생하는 코드들
func translate(using session: TranslationSession) async {
do {
var requests: [TranslationSession.Request] = []
let koreanRequest = TranslationSession.Request(sourceText: "안녕")
let englishRequest = TranslationSession.Request(sourceText: "no thanks")
let germanRequest = TranslationSession.Request(sourceText: "Guten Abend")
requests.append(koreanRequest)
requests.append(englishRequest)
requests.append(germanRequest)
for try await resposne in session.translate(batch: requests) {
print(resposne.targetText)
}
} catch {
}
}
// 출발어와 도착어가 같을 경우 번역 안되는 오류 발생
func translate(using session: TranslationSession) async {
do {
//let koreanRequests: [TranslationSession.Request] = [.init(sourceText: "안녕", clientIdentifier: "\(UUID())")]
let englishRequests: [TranslationSession.Request] = [TranslationSession.Request(sourceText: "no thanks")]
let germanRequests: [TranslationSession.Request] = [TranslationSession.Request(sourceText: "Guten Abend")]
//for try await resposne in session.translate(batch: koreanRequests) {
//print(resposne.targetText)
//}
for try await resposne in session.translate(batch: englishRequests) {
print(resposne.targetText)
}
for try await resposne in session.translate(batch: germanRequests) {
print(resposne.targetText)
}
} catch {
print(error.localizedDescription)
}
}
번역할 출발어와 도착어 선택하기
언어를 번역하기 위해서는 출발어와 도착어를 선택해야합니다.
- translationTask(source: target:)
- TranslationSession.Configuration.init(source: target:)
만약 출발어 source를 nil 로 설정하게될 경우 콘텐츠의 언어를 자동으로 식별하려고 시도합니다.
- 언어를 판단하지 못하면 선택하라는 메시지가 표시됩니다.
만약 도착어 target를 nil 로 설정하게될 경우 적절한 도착어가 선택 됩니다.
- 기본적으로 사용자의 기본 언어에 따라 도착어를 선택합니다.
언어 지정하기
언어를 선택하기 위해서는 언어는 다양한 형태로 설정할 수 있기에 까다로울 수 있습니다. Locale 과 Locale.Language 등이 사용
언어를 반환하는 API 에 따라 표준 언어일 수도 있고 번형어, 지역 방언 또는 겉보기에는 관련이 없어 보이는 지역 방언일 수도 있습니다.
NLLanguageRecognizer 로 언어 파악하기
콘텐츠가 어떤 언어인지 지정할 때에는 되도록 TranslationSession 에 Source 를 nil 로 지정해 언어를 자동으로 식별하는 것이 좋습니다. 하지만 어떤 언어인지 꼭 식별해야하는 경우 NLLanguageRecognizer 를 사용할 수 있습니다.
NLLanguageRecognizer를 사용한다면 현재 제공된 String 이 어떤 언어인지 확률적으로 계산하여 가장 확률 높은 값을 반환해줍니다.
func identifyLanguage(of sample: String) -> Locale.Language? {
let recognizer = NLLanguageRecognizer()
recognizer.processString(sample)
guard let language = recognizer.dominantLanguage else { return nil }
return Locale.Language(identifier: language.rawValue)
}
언어 번역하는 메서드들
같은 언어로 된 여러 문자열을 번역할 때는 단일 문자열 번역 함수보다는 묶음 번역 함수 중 하나를 사용하는 것이 가장 좋고 효율적입니다.
번역에서는 번역된 결과를 한번에 보여주는 것과 차례대로 보여주는 방법이 존재합니다.
물론 번역이 되는 대로 사용자한테 제공하는 것이 UI 응답 속도가 더 빠르다는 느낌을 주기 때문에 더 좋습니다.
// 모든 번역 작업이 완료되면 한번에 번역 결과를 반환
public func translations(from batch: [Request]) async throws -> [Response]
// 번역 작업이 완료되는 순서대로 번역 결과를 반환
public func translation(batch: [Request]) -> BatchResponse
번역 작업이 완료되는 순서대로 번역 결과를 즉시 반환하기 위해서는 각 요청에서 clientIdentifier 를 설정해야 합니다.
그래야 각 응답을 받을 때 어떤 결과를 받은 것인지 구별할 수 있습니다.
지원하는 번역 언어들
사용시 주의점
- 시뮬레이터에서는 작동을 하지 않으며 실제 기기에서 작동됩니다.
- transltionPresentation 와 translationTask 를 사용시 콘첸츠 자체에 첨부해야 합니다.
'SwiftUI' 카테고리의 다른 글
WWDC 20 - Meet WidgetKit (위젯 도입) (19) | 2024.10.09 |
---|---|
WidgetKit - 위젯 만들기 전에 알아둘 것들 (10) | 2024.10.07 |
WWDC20 Data Enssentials in SwiftUI (EnvironmentObject, ObservableObject, ObservedObject, StateObject, AppStorage, SceneStorage, 생명주기) (1) | 2024.10.03 |
SwiftUI - MapKit 에 대한 모든 것 (0) | 2024.09.08 |
SwiftUI - iCloud 사용하기 (0) | 2024.09.05 |