若要建立可重複使用的 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 也會顯示正確的代入值、複數形式或精確標籤。每當元件變更時,請一併執行這些檢查。

來源