對於包含框架自有面板的 Mac App,請在兩個層級定義回退行為:每個套件選擇哪種語言,以及查找失敗時每則訊息要傳回什麼。使用英文開發地區設定,並為您擁有的訊息明確設定英文預設值。

本指南會為一個簡易桌面視窗實作這項約定,其中包含 App 自有標籤 Workspace 和框架自有標籤 Panel ready。這些標籤是虛構的。目標是在語言或翻譯缺失時,仍讓訊息易於閱讀,同時在有翻譯時維持正常的本地化行為。

回退約定應保證什麼?

針對 App 自有和框架自有訊息採用以下約定:使用透過系統語言比對所選出的可用本地化版本;沒有任何偏好語言相符時,使用英文作為開發地區設定;字串查找失敗時,傳回該訊息明確設定的英文預設值。每則訊息都必須指出其所屬套件和字串表。

語言選擇會考量使用者的偏好語言和可用的本地化版本。第一順位語言缺失時,不代表系統一定會立即顯示英文:其他偏好語言可能會先比對成功。Apple 的封存套件指南說明了這項選擇順序,並指出若沒有偏好語言相符,系統接著會使用開發地區設定。目前的文件將 CFBundleDevelopmentRegion 定義為套件的回退語言識別碼。存取套件內容、CFBundleDevelopmentRegion。

定義預期結果時,請使用下表。它將資源選擇與訊息查找分開處理,而不把所有翻譯缺失的情況都視為相同狀況。

狀況 您所擁有訊息的約定 檢查項目
偏好語言有可用的翻譯 顯示該翻譯 套件、字串表和索引鍵
第一順位偏好語言不可用,但另一個偏好語言相符 允許系統語言比對選取可用的本地化版本 偏好順序和隨附的資源
沒有任何偏好本地化版本相符 使用套件的英文開發資源 開發語言和英文資源
選取的查找無法解析該訊息 傳回該訊息明確設定的英文預設值 該訊息的 defaultValue
框架查找意外指向 App 套件 修正套件引數 資源的所屬位置
翻譯存在,但文字不合適 修正翻譯 目錄內容和審核

此表結合了 Apple 的資源選擇規則與 Swift 初始化器文件所述的預設值行為。這是一項根據這些 API 制定的實作政策,並非字串目錄中的另一項回退功能。若查找成功但內容不合適,則不符合初始化器文件中使用預設值的條件:查找失敗。String.init(localized:defaultValue:table:bundle:locale:comment:)。

在每個所屬目標中設定英文來源資源

本範例使用目前版本的 Xcode、一個以 macOS 13 或更新版本為部署目標的 macOS SwiftUI App,以及一個名為 PanelKit 的內嵌動態 Swift 框架目標。將該框架連結並內嵌至 App。這些是範例專案的必要條件;框架名稱不代表現有產品。

將兩個目標的開發語言都設為英文,並確認每個建置後的套件其 CFBundleDevelopmentRegion 都解析為 en。在兩個目標中都提供相應的英文來源資源。開發語言識別碼描述的是回退語言;它不會補上缺少的訊息內容。Apple 將 en 文件化為英文語言識別碼。CFBundleDevelopmentRegion。

為每個目標分別建立一個 Localizable.xcstrings 目錄。在 Xcode 中選擇 File > New > File from Template,選取 String Catalog,並將其命名為 Localizable。將其中一個目錄指派給 App 目標,另一個指派給框架目標。分開管理資源所屬位置,才能獨立驗證每項查找。Apple 說明了建立目錄的流程,並支援多個目錄。使用字串目錄進行文字本地化和變化設定。

加入下方程式碼後,選擇 Product > Build。Xcode 會將可辨識的本地化呼叫擷取至目錄。確認 workspace.title 出現在 App 目錄中,而 panel.ready 出現在框架目錄中,且其英文來源值符合預期。使用目錄側邊欄的新增按鈕加入支援的語言,並為相關訊息輸入翻譯。使用字串目錄進行文字本地化和變化設定。

目錄是否完整與執行階段查找是否正確,需要分別檢查。框架目錄中的翻譯無法協助搜尋 App 套件的呼叫。Apple 明確指示,從主套件以外載入字串的程式碼,必須傳入與該目標相關聯的套件。準備 App 的文字以供翻譯。

為每則訊息指定套件、索引鍵、字串表和預設值

使用穩定的虛構索引鍵和易讀的英文預設值。接受 defaultValue 的 Swift 初始化器支援使用不同於顯示來源文字的索引鍵。若從套件查找本地化字串失敗,就會傳回其預設值。table 參數用來選取字串表;省略時會選取 Localizable。String.init(localized:defaultValue:table:bundle:locale:comment:)。

將框架訊息放在框架中

將下列完整的 Swift 原始碼檔案加入動態 PanelKit 框架目標。標記類別定義在該目標中,因此 Bundle(for:) 會識別其套件。Apple 將這種以類別為基礎的方法說明為目前 #bundle 巨集的替代方案。準備 App 的文字以供翻譯。

import Foundation

private final class PanelBundleAnchor: NSObject {}

public enum PanelText {
    public static var resourceBundle: Bundle {
        Bundle(for: PanelBundleAnchor.self)
    }

    public static var ready: String {
        String(
            localized: "panel.ready",
            defaultValue: "Panel ready",
            table: "Localizable",
            bundle: resourceBundle,
            comment: "Status label above the framework panel."
        )
    }
}

框架會在傳回 String 給 App 前解析其訊息。這明確劃分了所屬權:框架負責索引鍵、來源文字、字串表和資源套件;App 則取得解析完成的顯示文字。

同時顯示 App 和框架訊息

將產生的 App 進入點檔案替換為下列完整範例。此範例假設連結的框架模組名稱為 PanelKit,且其中包含前一個檔案。App 目標中只保留一個 App 進入點。

import Foundation
import SwiftUI
import PanelKit

@main
struct FallbackDemoApp: App {
    var body: some Scene {
        WindowGroup {
            VStack(alignment: .leading, spacing: 12) {
                Text(verbatim: String(
                    localized: "workspace.title",
                    defaultValue: "Workspace",
                    table: "Localizable",
                    bundle: .main,
                    comment: "Heading for the main workspace."
                ))

                Text(verbatim: PanelText.ready)
            }
            .padding(24)
        }
    }
}

App 的查找明確使用 .main;框架的查找則使用自己的資源套件。兩者都提供英文預設值。這符合 Apple 文件所述的套件選擇和預設值參數。請在 Xcode 中建置並執行專案,以驗證完整的目標設定;此處並未執行這些範例。String.init(localized:defaultValue:table:bundle:locale:comment:)。

對於現有的 Bundle.localizedString(forKey:value:table:) 實作,若需要易讀的缺少索引鍵結果,請提供非空且易讀的 value。Apple 說明,若 value 缺失或為空,缺少的索引鍵會傳回索引鍵本身;若它不是空值,則會傳回 value。因此,像 panel.ready 這樣的索引鍵若顯示該識別碼會違反訊息約定,就需要明確指定回退值。localizedString(forKey:value:table:)。

決定框架是否可以選取不同語言

傳入正確的套件,就能指定搜尋位置。混合本地化設定會影響本地化選擇與所屬 App 之間的關係。Apple 提供布林值 CFBundleAllowMixedLocalizations,顯示名稱為 Localized resources can be mixed,供取得框架的本地化字串時使用。CFBundleAllowMixedLocalizations。

Swift Forums 對此行為的說明指出,在 App 的 Info.plist 中啟用此索引鍵,可讓其他套件選取主 App 未支援的本地化版本。若未啟用,主套件會限制它們的選擇。這是一篇較早的說明討論,可與 Apple 目前的索引鍵參考文件搭配閱讀。Swift 套件管理員本地化。

對於範例視窗,建議 App 和框架支援相同的語言範圍,並在兩者中都提供英文開發資源。初始約定不包含框架獨立選取語言的行為。若您預期框架使用額外翻譯,請將 App 的 CFBundleAllowMixedLocalizations 布林值設為 YES,並明確測試此設定。Swift 套件管理員本地化。

啟用此索引鍵不會提供翻譯,也不會設定缺少訊息時的預設值。這些工作仍由資源檔和查找呼叫負責。同樣地,在您自己的呼叫中提供預設值,只會定義該呼叫查找失敗時的結果;它不會為每個系統控制項或框架訊息建立通用的英文回退設定。

請勿將此 String 初始化器的 locale 引數用作翻譯語言切換器。Apple 明確說明,這個引數會控制插值值(例如數字)的本地化,不會改變查找本地化字串時使用的地區設定。請使用本任務所需的支援語言設定與測試控制項。String.init(localized:defaultValue:table:bundle:locale:comment:)。

分別測試缺少語言和缺少訊息

請透過 Product > Scheme > Edit Scheme 執行本地化檢查。選取 Run,開啟 Options,選擇 App Language,關閉對話框,然後重新執行 Mac App。Apple 說明了這套流程,讓您無須更改整個系統的設定即可測試本地化版本。調查僅與語言有關的問題時,請保持地區設定不變。執行 App 時測試本地化版本。

使用下列測試矩陣作為專案專用的診斷輔助工具。檢查套件路由時,請在支援但非來源語言的本地化版本中,暫時使用不同的英文測試標記,例如 App translation sample 和 Framework translation sample。這些是診斷內容,不是翻譯或語言識別碼。

  1. 驗證來源語言行為。 選取英文執行。預期會顯示 Workspace 和 Panel ready。
  2. 驗證翻譯資源路由。 選取支援的測試本地化版本。預期會顯示兩個所屬目錄中的不同標記。
  3. 驗證缺少訊息時的預設值。 在可丟棄的測試修改中,將框架查找索引鍵改為 panel.ready.probe,保持 defaultValue 不變,並確保該探測項目不會包含在測試目錄中。預期查找失敗時會顯示 Panel ready。之後還原原始索引鍵。
  4. 驗證不支援語言的選擇結果。 使用不含任何支援語言相符項目的偏好語言清單進行測試。確認兩個所屬套件都使用英文開發資源。另外也要測試第一順位偏好語言不受支援、但後續偏好語言受支援的情況,因為其預期結果不同。
  5. 若啟用混合本地化,請加以驗證。 使用只有框架支援的語言進行測試。記錄預期行為,並據此測試 App 的混合本地化設定。

第三項檢查會隔離查找失敗的情況;第四項則會隔離語言比對。只測試目錄中未翻譯的項目,可能會混淆這兩種情況,因為產生的資源設定也會影響查找。缺少索引鍵時的預期結果來自 Swift 初始化器,而偏好語言的選擇則遵循套件規則。String.init(localized:defaultValue:table:bundle:locale:comment:)、存取套件內容。

若結果不符預期,請檢查實際的所屬套件。developmentLocalization 會回報與 CFBundleDevelopmentRegion 對應的值;preferredLocalizations 會依照語言偏好順序回報可用的本地化版本。在 Bundle.main 和 PanelText.resourceBundle 上比較這些屬性。它們有助於診斷設定,但最終仍須檢查實際顯示的訊息,確認每項查找結果。developmentLocalization、preferredLocalizations。

完善的回退約定能讓每個結果都清楚可解釋:所選語言來自套件比對,訊息來自其所屬字串表,而查找失敗時會傳回明確且易讀的預設值。請將這些規則套用到您擁有的每則 App 和框架訊息,並在使用者實際會看到它們的桌面視窗中一併驗證。

來源