若要建立可重複使用的 macOS 標籤,請以 LocalizedStringResource 接受可本地化訊息,為純文字保留獨立的 verbatim: 路徑,並將插值保留到本地化時再處理。本教學將為桌面項目計數器建立這項契約,並分別檢查字串擷取與顯示。
可重複使用的文字 API 應該做出什麼承諾?
假設 Mac app 需要一個簡潔摘要,顯示項目數量和合成的集合標籤,例如 Sample Set。可重複使用的元件必須區分三種輸入:需要翻譯的 app 撰寫訊息、既有的 SwiftUI 本地化鍵,以及必須完全依照提供內容顯示的文字。
Apple 建議使用 LocalizedStringResource 表示可本地化字串,並在自訂檢視和其他 Swift 程式碼中傳遞這類字串。目前的指引示範如何將此型別的自訂檢視屬性交由 Text 顯示。設計新元件時,請以它作為主要訊息契約。準備 app 文字以供翻譯。
下方的決策表是根據已記載行為衍生出的 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。
使用明確的進入點建置完整元件
在 Xcode 26 或更新版本中使用 macOS SwiftUI app 專案,並將部署目標設為 macOS 26。本範例請以以下完整程式碼取代產生的 app 和內容檢視宣告;目標中只保留一個 @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。執行時,app 會解析訊息並代入引數。Apple 的 String Catalog 說明課程明確描述,系統會在自訂檢視的呼叫位置辨識用於建立 LocalizedStringResource 的字串常值。課程也提到 Use Compiler to Extract Swift Strings 建置設定。探索 String Catalog。
插值必須先傳遞到支援本地化的型別,再轉成一般的完整字串。Apple 說明,SwiftUI 的插值建構器會將格式鍵和引數值分開保留,讓系統能先進行本地化,再代入數值。將已插值的 String 轉成鍵,無法還原原本具型別的引數。Swift 新功能。
以下診斷片段用來說明差異,並非另一個 app。片段中使用的所有值都在本機定義。
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 已將該變數所有可能的值擷取到目錄中。請明確維護刻意使用的動態鍵,或為 app 擁有的有限訊息使用字串常值資源宣告。關於查詢行為和後備值,請參閱 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 參數可能已在訊息傳到元件前,抹去了可本地化運算式。請檢查目錄的目標、建置包含該呼叫位置的 scheme,並確認已啟用 Use Compiler to Extract Swift Strings。Apple 說明,擷取會依據目前的 scheme 和平台執行,因此請檢查實際編譯該訊息的組態。探索 String Catalog。
如果項目存在,但 app 顯示的是鍵或後備文字,請檢查查詢設定。以鍵為基礎的 Text 初始化器會搜尋指定的字串表和套件;省略值時,會使用預設字串表和主要套件。若找不到鍵或字串表,就會顯示該鍵的字串表示。因此,可讀的後備文字可能掩蓋查詢設定不完整的問題。Text.init(_:tableName:bundle:comment:)。
將元件移入架構或套件時,請決定訊息的歸屬。元件擁有的訊息需要使用元件的資源套件;呼叫端擁有的資源則應保留提供的查詢資訊。Apple 目前的指引支援在本地化 API 中傳入 #bundle,以指定包含該目標的套件。具名字串表也需要對應的目錄。準備 app 文字以供翻譯。
最後,請勿假設 SwiftUI 接受的所有插值都能用於 Foundation 資源。LocalizedStringKey 有自己的插值方法,包括格式樣式和格式化器變體。組成運算式前,請先選擇訊息型別;若某個引數無法編譯,請查閱該型別支援的插值操作。LocalizedStringKey.StringInterpolation。
當呼叫端能清楚表達意圖,並能在兩個階段都完成驗證時,可重複使用的文字元件便準備就緒:建置會產生預期的目錄訊息,執行中的 Mac app 也會顯示正確的代入值、複數形式或精確標籤。每當元件變更時,請一併執行這些檢查。
來源
- 準備 app 文字以供翻譯 — 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