재사용 가능한 macOS 레이블에는 현지화 가능한 메시지를 LocalizedStringResource로 받고, 리터럴 텍스트를 위한 별도의 verbatim: 경로를 유지하며, 현지화가 완료될 때까지 보간을 보존하세요. 이 튜토리얼에서는 데스크톱 항목 카운터를 위해 이러한 계약을 구현하고, 문자열 추출과 화면 표시를 별도로 확인합니다.
재사용 가능한 텍스트 API는 무엇을 보장해야 할까요?
Mac 앱에 항목 수와 Sample Set 같은 임의의 컬렉션 레이블을 보여 주는 간결한 요약이 필요하다고 가정해 보겠습니다. 재사용 가능한 컴포넌트는 세 가지 입력을 구분해야 합니다. 번역이 필요한 앱 작성 메시지, 기존 SwiftUI 현지화 키, 그리고 제공된 그대로 정확히 표시해야 하는 텍스트입니다.
Apple은 사용자 지정 뷰와 다른 Swift 코드에서 현지화 가능한 문자열을 표현하고 전달할 때 LocalizedStringResource를 사용할 것을 권장합니다. 현재 지침에서는 이 형식의 사용자 지정 뷰 속성을 Text로 표시하는 방법을 보여 줍니다. 새 컴포넌트를 설계할 때 기본 메시지 계약으로 사용하세요. 앱의 텍스트를 번역에 맞게 준비하기.
다음 의사 결정 표는 문서화된 동작을 바탕으로 설계한 API입니다. 명시적인 인자 레이블을 사용하면 서로 경쟁하는 레이블 없는 두 문자열 오버로드에 의존하지 않고도 호출자의 의도를 검토할 수 있습니다.
| 호출자의 의도 | 제안하는 진입점 | 렌더링 경로 |
|---|---|---|
| 선택적 컨텍스트를 포함해 번역 가능한 메시지 전달 | SummaryLabel(localized:) |
Text(resource) |
| SwiftUI 보간을 포함한 SwiftUI 키 전달 | SummaryLabel(key:) |
Text(key, tableName:bundle:) |
| 정확한 레이블 표시 | SummaryLabel(verbatim:) |
Text(verbatim:) |
| 호출자가 이미 조합한 텍스트 제공 | SummaryLabel(text:) |
전달된 Text 저장 |
컴포넌트에 SwiftUI의 키 기반 동작이 필요한 경우 LocalizedStringKey를 사용할 수 있게 두세요. Apple 문서에 따르면 문자열 리터럴을 Text에 직접 전달하면 현지화 키가 생성되지만, String 변수는 보통 현지화를 우회하는 경로를 사용합니다. 따라서 String만 받는 래퍼는 겉보기에 비슷한 호출의 의미를 바꿉니다. LocalizedStringKey.
명시적 진입점을 갖춘 완전한 컴포넌트 만들기
이 예제에서는 배포 대상이 macOS 26으로 설정된 Xcode 26 이상의 macOS SwiftUI 앱 프로젝트를 사용합니다. 생성된 앱 및 콘텐츠 뷰 선언을 아래의 완전한 프로그램으로 교체하고, 타깃에는 @main 선언을 하나만 남겨 두세요. 모든 레이블과 수치는 임의의 예시입니다. 문서화된 API를 기준으로 코드를 검토했지만, 이 환경에는 Swift 컴파일러가 없어 컴파일하지 않았습니다.
import Foundation
import SwiftUI
struct SummaryLabel: View {
private let text: Text
init(localized resource: LocalizedStringResource) {
text = Text(resource)
}
init(
key: LocalizedStringKey,
tableName: String? = nil,
bundle: Bundle? = nil,
comment: StaticString? = nil
) {
text = Text(
key,
tableName: tableName,
bundle: bundle,
comment: comment
)
}
init(verbatim value: String) {
text = Text(verbatim: value)
}
init(text: Text) {
self.text = text
}
var body: some View {
text
.font(.callout)
.padding(8)
}
}
struct ContentView: View {
@State private var itemCount = 2
private let collectionLabel = "Sample Set"
var body: some View {
VStack(alignment: .leading, spacing: 12) {
Stepper(value: $itemCount, in: 0...12) {
Text("Item count")
}
SummaryLabel(
localized: LocalizedStringResource(
"summary.item-count",
defaultValue: "\(itemCount) items",
comment: "Summary below the item-count control. The integer is the number of items."
)
)
SummaryLabel(
key: "Collection: \(collectionLabel)"
)
SummaryLabel(verbatim: collectionLabel)
SummaryLabel(
text: Text(
"Preview",
comment: "Heading for a preview of the current item selection."
).bold()
)
}
.padding(20)
.frame(minWidth: 320)
}
}
@main
struct TextContractDemoApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
리소스 이니셜라이저는 안정적인 키 summary.item-count와 기본 메시지 \(itemCount) items를 의도적으로 분리합니다. Apple은 이 이니셜라이저에 정적 키, 현지화 가능한 기본값, 테이블, 로캘, 번들, 번역자 주석을 사용할 수 있다고 설명합니다. 지정된 테이블에 키가 없으면 기본값이 사용됩니다. LocalizedStringResource 이니셜라이저.
verbatim: 이니셜라이저는 제공된 문자열을 현지화하지 않고 표시한다는 점을 명확히 보장합니다. 리터럴 인자와 변수 모두에 사용할 수 있습니다. 정확한 철자 그대로 표시하는 것이 중요한 임의의 컬렉션 레이블에는 이 경로를 사용하세요. Text.init(verbatim:).
text: 진입점을 사용하면 호출자가 현지화되거나 스타일이 적용된 텍스트를 구성한 뒤 전달할 수 있습니다. 이 컴포넌트는 공통 타이포그래피와 간격을 더하지만, 메시지 의미는 호출자가 선택합니다. Text를 전달하는 것은 탈출구이므로 더 좁은 계약을 강제해야 하는 컴포넌트에서는 이 이니셜라이저를 생략할 수 있습니다.
추출과 런타임 서식 지정은 어떻게 다를까요?
현지화를 두 가지 별도 확인 사항으로 다루세요. 빌드 중에 Xcode가 현지화 가능한 소스 표현식을 찾아 String Catalog를 업데이트합니다. 실행 중에는 앱이 메시지를 확인하고 인자를 대체합니다. Apple의 String Catalog 세션에서는 사용자 지정 뷰 호출 위치에서 LocalizedStringResource를 초기화하는 데 사용된 리터럴을 인식하는 방법을 설명합니다. 또한 Use Compiler to Extract Swift Strings 빌드 설정도 소개합니다. String Catalog 알아보기.
보간 결과가 일반적인 완성 문자열이 되기 전에 현지화를 인식하는 형식에 도달해야 합니다. Apple은 SwiftUI의 보간 빌더가 서식 키와 인자 값을 분리해 두므로 대체 전에 현지화할 수 있다고 설명합니다. 이미 보간된 String을 키로 변환해도 원래의 형식 지정된 인자를 복원할 수는 없습니다. Swift의 새로운 기능.
다음 진단 코드 조각은 차이를 보여 줍니다. 두 번째 앱은 아닙니다. 코드 조각에 쓰인 값은 모두 해당 위치에서 정의합니다.
import SwiftUI
let count = 2
// Ordinary String interpolation has already produced "2 items".
let completed: String = "\(count) items"
let exactDisplay = Text(completed)
// Looks up the completed value as a key.
// This does not restore a count placeholder.
let completedValueLookup = Text(LocalizedStringKey(completed))
// Preserves localization-aware interpolation from the beginning.
let messageKey: LocalizedStringKey = "\(count) items"
let interpolatedDisplay = Text(messageKey)
변수에서 LocalizedStringKey를 만드는 것은 런타임 조회를 요청하는 문서화된 방법입니다. 변수에 알려진 키가 의도적으로 들어 있을 때 유용합니다. 그렇다고 Xcode가 해당 변수의 가능한 모든 값을 카탈로그에 추출했다는 뜻은 아닙니다. 동적 키는 명시적으로 관리하거나, 앱에서 사용하는 한정된 메시지에는 리터럴 리소스 선언을 사용하세요. 조회 동작과 대체 동작은 Text.init(_:tableName:bundle:comment:)에 설명되어 있습니다.
문장부호와 자리표시자 위치를 포함해 메시지 전체를 하나의 현지화 가능한 표현식으로 유지하세요. 이 예제에서는 Collection: \(collectionLabel)을 하나로 유지합니다. 각각 확인한 조각을 이어 완성 메시지를 만들면 순서와 간격을 호출자가 책임져야 합니다.
카탈로그를 추가하고 복수형 동작 구성하기
먼저 File > New > File from Template을 통해 String Catalog를 추가하고 String Catalog를 선택한 다음 이름을 Localizable로 지정하세요. 그런 다음 Product > Build를 선택합니다. Apple 문서에 따르면 빌드 시 현지화 API를 바탕으로 카탈로그가 채워지며, 소스 메시지가 바뀌면 업데이트됩니다. 문자열 카탈로그로 텍스트 현지화 및 변형하기.
이 예제에서는 summary.item-count 항목과 보간된 정수를 확인합니다. 항목을 Control 키를 누른 채 클릭하고 Vary by Plural을 선택하세요. 영어 원본 현지화에서 One을 %lld item, Other를 %lld items로 설정하고 Xcode가 제공한 정수 자리표시자는 그대로 유지합니다. 다른 현지화를 추가할 때는 필요한 형식을 추가하세요. 복수형 범주는 언어에 따라 다릅니다. 문자열 카탈로그로 텍스트 현지화 및 변형하기.
보간은 수를 전달하지만, 그것만으로 단수형 문구가 만들어지지는 않습니다. Apple의 코드 따라 하기 세션에서는 카탈로그에 복수형 변형을 추가하면 런타임에 시스템이 일치하는 변형을 선택하는 방법을 보여 줍니다. 따라서 빌드가 성공하고 메시지가 추출된 것은 확인 과정의 일부일 뿐입니다. 코드 따라 하기: Xcode로 현지화 살펴보기.
원본 복수형을 구성한 뒤에는 다음의 간단한 승인 기준표를 사용하세요. 이는 예시 프로그램에서 기대되는 결과이며, 실제 실행 결과를 보고한 것이 아닙니다.
| 확인 항목 | 예상 결과 |
|---|---|
빌드한 뒤 summary.item-count 확인 |
정수 대체가 포함된 메시지 항목 하나 |
수를 0로 설정 |
0 items |
수를 1로 설정 |
1 item |
수를 2로 설정 |
2 items |
| 정확한 레이블이 표시되는 행 확인 | Sample Set이 리터럴로 유지됨 |
| 컬렉션 메시지 확인 | 레이블이 완전한 메시지 안의 인자로 사용됨 |
누락된 항목과 잘못된 표시를 따로 진단하기
카탈로그에서 메시지가 누락되면 먼저 소스 형식을 확인하세요. String 매개변수는 현지화 가능한 표현식이 컴포넌트에 도달하기 전에 이를 제거했을 수 있습니다. 카탈로그의 타깃을 확인하고, 해당 호출 위치를 포함하는 스킴을 빌드한 다음, Use Compiler to Extract Swift Strings가 활성화되어 있는지 확인하세요. Apple은 현재 스킴과 플랫폼을 기준으로 추출이 이루어진다고 설명하므로, 실제로 메시지를 컴파일하는 구성을 확인해야 합니다. String Catalog 알아보기.
항목은 있지만 앱에 키나 대체 텍스트가 표시되면 조회 구성을 확인하세요. 키 기반 Text 이니셜라이저는 지정된 테이블과 번들을 검색합니다. 값을 생략하면 기본 테이블과 메인 번들이 선택됩니다. 키나 테이블이 없으면 키의 문자열 표현이 표시됩니다. 따라서 읽을 수 있는 대체 텍스트가 불완전한 조회를 가릴 수 있습니다. Text.init(_:tableName:bundle:comment:).
컴포넌트를 프레임워크나 패키지로 옮길 때는 메시지를 어디에 둘지 결정하세요. 컴포넌트 소유 메시지에는 컴포넌트의 리소스 번들이 필요합니다. 호출자 소유 리소스에는 전달된 조회 정보가 유지되어야 합니다. Apple의 최신 지침은 현지화 API에 #bundle을 전달해 해당 타깃을 포함하는 번들을 지정하는 방법을 지원합니다. 이름이 지정된 테이블에도 이에 맞는 카탈로그가 필요합니다. 앱의 텍스트를 번역에 맞게 준비하기.
마지막으로 SwiftUI에서 허용되는 모든 보간이 Foundation 리소스에서도 사용 가능하다고 가정하지 마세요. LocalizedStringKey에는 서식 지정 및 포매터 변형을 포함한 자체 보간 메서드가 있습니다. 표현식을 작성하기 전에 메시지 형식을 선택하고, 인자 컴파일 오류가 발생하면 지원되는 보간 연산을 확인하세요. LocalizedStringKey.StringInterpolation.
호출자가 의도를 명확히 표현할 수 있고, 두 단계에서 모두 확인할 수 있다면 재사용 가능한 텍스트 컴포넌트가 준비된 것입니다. 빌드 결과로 의도한 카탈로그 메시지가 생성되고, 실행 중인 Mac 앱에서 올바른 대체 문구, 복수형, 또는 정확한 레이블이 표시되어야 합니다. 컴포넌트가 바뀔 때마다 이러한 확인 사항을 API 계약과 함께 유지하세요.
출처
- 앱의 텍스트를 번역에 맞게 준비하기 — Apple
- LocalizedStringKey — Apple
- init(_:defaultValue:table:locale:bundle:comment:) — Apple
- init(verbatim:) — Apple
- String Catalog 알아보기 — Apple
- Swift의 새로운 기능 — Apple
- init(_:tableName:bundle:comment:) — Apple
- 문자열 카탈로그로 텍스트 현지화 및 변형하기 — Apple
- 코드 따라 하기: Xcode로 현지화 살펴보기 — Apple
- LocalizedStringKey.StringInterpolation — Apple