對於包含框架自有面板的 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。這些是診斷內容,不是翻譯或語言識別碼。
- 驗證來源語言行為。 選取英文執行。預期會顯示
Workspace和Panel ready。 - 驗證翻譯資源路由。 選取支援的測試本地化版本。預期會顯示兩個所屬目錄中的不同標記。
- 驗證缺少訊息時的預設值。 在可丟棄的測試修改中,將框架查找索引鍵改為
panel.ready.probe,保持defaultValue不變,並確保該探測項目不會包含在測試目錄中。預期查找失敗時會顯示Panel ready。之後還原原始索引鍵。 - 驗證不支援語言的選擇結果。 使用不含任何支援語言相符項目的偏好語言清單進行測試。確認兩個所屬套件都使用英文開發資源。另外也要測試第一順位偏好語言不受支援、但後續偏好語言受支援的情況,因為其預期結果不同。
- 若啟用混合本地化,請加以驗證。 使用只有框架支援的語言進行測試。記錄預期行為,並據此測試 App 的混合本地化設定。
第三項檢查會隔離查找失敗的情況;第四項則會隔離語言比對。只測試目錄中未翻譯的項目,可能會混淆這兩種情況,因為產生的資源設定也會影響查找。缺少索引鍵時的預期結果來自 Swift 初始化器,而偏好語言的選擇則遵循套件規則。String.init(localized:defaultValue:table:bundle:locale:comment:)、存取套件內容。
若結果不符預期,請檢查實際的所屬套件。developmentLocalization 會回報與 CFBundleDevelopmentRegion 對應的值;preferredLocalizations 會依照語言偏好順序回報可用的本地化版本。在 Bundle.main 和 PanelText.resourceBundle 上比較這些屬性。它們有助於診斷設定,但最終仍須檢查實際顯示的訊息,確認每項查找結果。developmentLocalization、preferredLocalizations。
完善的回退約定能讓每個結果都清楚可解釋:所選語言來自套件比對,訊息來自其所屬字串表,而查找失敗時會傳回明確且易讀的預設值。請將這些規則套用到您擁有的每則 App 和框架訊息,並在使用者實際會看到它們的桌面視窗中一併驗證。
來源
- 準備 App 的文字以供翻譯 — Apple
- 使用字串目錄進行文字本地化和變化設定 — Apple
- CFBundleDevelopmentRegion — Apple
- String.init(localized:defaultValue:table:bundle:locale:comment:) — Apple
- localizedString(forKey:value:table:) — Apple
- CFBundleAllowMixedLocalizations — Apple
- Swift 套件管理員本地化 — Swift Forums
- 執行 App 時測試本地化版本 — Apple
- developmentLocalization — Apple
- preferredLocalizations — Apple
- 存取套件內容 — Apple 文件封存