Legen Sie bei einer Mac-App mit Framework-eigenen Fenstern den Fallback auf zwei Ebenen fest: welche Sprache jedes Bundle auswählt und was jede Nachricht zurückgibt, wenn ihre Suche fehlschlägt. Verwenden Sie eine englische Entwicklungslokalisierung und explizite englische Standardwerte für Ihre eigenen Nachrichten.

In dieser Anleitung wird dieser Vertrag für ein einfaches Desktopfenster umgesetzt, das die App-eigene Beschriftung Workspace und die Framework-eigene Beschriftung Panel ready enthält. Die Beschriftungen sind fiktiv. Ziel ist es, diese Nachrichten lesbar zu halten, wenn eine Sprache oder Übersetzung fehlt, und zugleich die normale Lokalisierung beizubehalten, wenn Übersetzungen vorhanden sind.

Was sollte der Fallback-Vertrag zusichern?

Legen Sie für App-eigene und Framework-eigene Nachrichten folgenden Vertrag fest: Verwenden Sie eine verfügbare Lokalisierung, die anhand des Sprachabgleichs des Systems ausgewählt wird; verwenden Sie Englisch als Entwicklungslokalisierung, wenn keine bevorzugte Lokalisierung übereinstimmt; und geben Sie den expliziten englischen Standardwert einer Nachricht zurück, wenn die Suche nach ihrem String fehlschlägt. Jede Nachricht muss ihr zuständiges Bundle und ihre String-Tabelle angeben.

Bei der Sprachauswahl werden die bevorzugten Sprachen der Nutzer und die verfügbaren Lokalisierungen berücksichtigt. Wenn die Sprache erster Wahl fehlt, wird nicht unbedingt sofort Englisch angezeigt: Zuerst kann eine andere bevorzugte Sprache übereinstimmen. Apples archivierte Bundle-Anleitung beschreibt diese Auswahlfolge und anschließend die Entwicklungslokalisierung, wenn keine bevorzugte Sprache übereinstimmt. Die aktuelle Dokumentation definiert CFBundleDevelopmentRegion als Sprachkennung des Fallbacks des Bundles. Auf die Inhalte eines Bundles zugreifen, CFBundleDevelopmentRegion.

Verwenden Sie die folgende Entscheidungstabelle, um erwartete Ergebnisse festzulegen. Sie unterscheidet die Ressourcenauswahl von der Nachrichtensuche, statt jede fehlende Übersetzung als denselben Fall zu behandeln.

Bedingung Vertrag für Ihre eigenen Nachrichten Zu prüfen
Für eine bevorzugte Sprache ist eine Übersetzung verfügbar Diese Übersetzung anzeigen Bundle, Tabelle und Schlüssel
Die Sprache erster Wahl ist nicht verfügbar, aber eine andere bevorzugte Sprache stimmt überein Dem System überlassen, die verfügbare Lokalisierung anhand des Sprachabgleichs auszuwählen Reihenfolge der bevorzugten Sprachen und ausgelieferte Ressourcen
Keine bevorzugte Lokalisierung stimmt überein Die englischen Entwicklungsressourcen des Bundles verwenden Entwicklungssprache und englische Ressourcen
Die ausgewählte Suche kann die Nachricht nicht auflösen Ihren expliziten englischen Standardwert zurückgeben defaultValue der Nachricht
Eine Framework-Suche richtet sich versehentlich an das App-Bundle Das Bundle-Argument korrigieren Ressourcenzuständigkeit
Eine Übersetzung ist vorhanden, enthält aber ungeeigneten Text Die Übersetzung korrigieren Kataloginhalt und Überprüfung

Die Tabelle verbindet Apples Regeln zur Ressourcenauswahl mit dem dokumentierten Verhalten des Standardwerts im Swift-Initializer. Sie ist eine aus diesen APIs abgeleitete Implementierungsrichtlinie und keine separate Fallback-Funktion in String Catalogs. Eine erfolgreiche Suche, die ungeeigneten Text zurückgibt, erfüllt nicht die dokumentierte Bedingung für die Verwendung des Standardwerts im Initializer: eine fehlgeschlagene Suche. String.init(localized:defaultValue:table:bundle:locale:comment:).

Englische Quellressourcen in jedem zuständigen Target konfigurieren

Verwenden Sie für dieses Beispiel eine aktuelle Xcode-Installation, eine macOS-SwiftUI-App mit einem Ziel von macOS 13 oder neuer sowie ein eingebettetes dynamisches Swift-Framework-Target namens PanelKit. Verknüpfen Sie dieses Framework mit der App und betten Sie es ein. Dies sind Voraussetzungen für das Beispielprojekt; der Framework-Name bezeichnet kein vorhandenes Produkt.

Legen Sie für beide Targets Englisch als Entwicklungssprache fest und überprüfen Sie, dass CFBundleDevelopmentRegion im jeweiligen erstellten Bundle zu en aufgelöst wird. Stellen Sie die entsprechenden englischen Quellressourcen in beiden Targets bereit. Eine Kennung der Entwicklungssprache bezeichnet die Fallback-Sprache; sie stellt keine fehlenden Nachrichteninhalte bereit. Apple dokumentiert en als englische Sprachkennung. CFBundleDevelopmentRegion.

Erstellen Sie für jedes Target einen eigenen Katalog Localizable.xcstrings. Wählen Sie in Xcode Datei > Neu > Datei aus Vorlage, wählen Sie String Catalog und nennen Sie ihn Localizable. Weisen Sie einen Katalog dem App-Target und den anderen dem Framework-Target zu. Durch die getrennte Zuständigkeit für Ressourcen lässt sich jede Suche unabhängig überprüfen. Apple dokumentiert den Ablauf zum Erstellen von Katalogen und unterstützt mehrere Kataloge. Text mit einem String-Katalog lokalisieren und variieren.

Nachdem Sie den folgenden Code hinzugefügt haben, wählen Sie Produkt > Erstellen. Xcode extrahiert erkannte lokalisierbare Aufrufe in die Kataloge. Vergewissern Sie sich, dass workspace.title im App-Katalog und panel.ready im Framework-Katalog mit den vorgesehenen englischen Quellwerten erscheint. Fügen Sie unterstützte Sprachen über die Schaltfläche zum Hinzufügen in der Katalogseitenleiste hinzu und tragen Sie Übersetzungen für die relevanten Nachrichten ein. Text mit einem String-Katalog lokalisieren und variieren.

Die Vollständigkeit des Katalogs und die Korrektheit der Laufzeitsuche müssen getrennt überprüft werden. Ein übersetzter Eintrag im Framework-Katalog hilft nicht, wenn ein Aufruf das App-Bundle durchsucht. Apple weist ausdrücklich darauf hin, dass Code beim Laden von Strings außerhalb des Haupt-Bundles das dem jeweiligen Target zugeordnete Bundle übergeben muss. Den Text Ihrer App für die Übersetzung vorbereiten.

Für jede Nachricht Bundle, Schlüssel, Tabelle und Standardwert angeben

Verwenden Sie stabile fiktive Schlüssel und gut lesbare englische Standardwerte. Der Swift-Initializer mit defaultValue unterstützt einen Schlüssel, der sich vom angezeigten Quelltext unterscheidet. Der Standardwert wird zurückgegeben, wenn die Suche nach dem lokalisierten String im Bundle fehlschlägt. Der Parameter table wählt die String-Tabelle aus; wenn Sie ihn weglassen, wird Localizable verwendet. String.init(localized:defaultValue:table:bundle:locale:comment:).

Framework-Nachrichten im Framework ablegen

Fügen Sie diese vollständige Swift-Quelldatei dem dynamischen Framework-Target PanelKit hinzu. Die Marker-Klasse ist in diesem Target definiert, damit Bundle(for:) sein Bundle ermittelt. Apple dokumentiert diesen klassenbasierten Ansatz als Alternative zum aktuellen Makro #bundle. Den Text Ihrer App für die Übersetzung vorbereiten.

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."
        )
    }
}

Das Framework löst seine Nachricht auf, bevor es der App einen String zurückgibt. So entsteht eine klare Zuständigkeitsgrenze: Das Framework legt Schlüssel, Quelltext, Tabelle und Ressourcen-Bundle fest. Die App erhält den resultierenden Anzeigetext.

App- und Framework-Nachrichten gemeinsam anzeigen

Ersetzen Sie die generierte App-Einstiegsdatei durch dieses vollständige Beispiel. Es setzt voraus, dass das verknüpfte Framework-Modul PanelKit heißt und die vorherige Datei enthält. Lassen Sie im App-Target nur einen App-Einstiegspunkt.

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)
        }
    }
}

Bei der App-Suche wird ausdrücklich .main verwendet; bei der Framework-Suche wird das eigene Ressourcen-Bundle genutzt. Für beide sind englische Standardwerte angegeben. Das entspricht Apples dokumentierten Parametern für die Bundle-Auswahl und den Standardwert. Erstellen Sie das Projekt in Xcode und führen Sie es aus, um die vollständige Target-Konfiguration zu validieren; diese Beispiele wurden hier nicht ausgeführt. String.init(localized:defaultValue:table:bundle:locale:comment:).

Wenn eine vorhandene Implementierung Bundle.localizedString(forKey:value:table:) verwendet, geben Sie einen nicht leeren, gut lesbaren Wert für value an, wenn Sie für fehlende Schlüssel ein lesbares Ergebnis benötigen. Apple dokumentiert, dass bei einem fehlenden Schlüssel der Schlüssel selbst zurückgegeben wird, wenn value fehlt oder leer ist, und value, wenn der Wert nicht leer ist. Ein Schlüssel wie panel.ready benötigt daher einen expliziten Fallback, wenn die Offenlegung dieser Kennung gegen den Nachrichtenvertrag verstoßen würde. localizedString(forKey:value:table:).

Festlegen, ob Frameworks unterschiedliche Sprachen auswählen dürfen

Das richtige Bundle als Argument gibt an, wo gesucht werden soll. Die Konfiguration gemischter Lokalisierungen beeinflusst, wie sich die Lokalisierungsauswahl zum umgebenden App-Bundle verhält. Apple stellt für das Abrufen lokalisierter Strings aus Frameworks den booleschen Schlüssel CFBundleAllowMixedLocalizations bereit, der als Lokalisierte Ressourcen können gemischt werden angezeigt wird. CFBundleAllowMixedLocalizations.

Laut der Erklärung zu diesem Verhalten in den Swift-Foren kann durch Aktivieren des Schlüssels in der Info.plist der App anderen Bundles gestattet werden, Lokalisierungen auszuwählen, die von der Haupt-App nicht unterstützt werden. Ohne diese Aktivierung schränkt das Haupt-Bundle ihre Auswahl ein. Diese ältere Erläuterung ergänzt Apples aktuelle Referenz zum Schlüssel. Lokalisierung im Swift-Paketmanager.

Bevorzugen Sie für das Beispielfenster eine übereinstimmende Abdeckung unterstützter Sprachen in App und Framework, mit englischen Entwicklungsressourcen in beiden. Lassen Sie die unabhängige Sprachauswahl des Frameworks zunächst außerhalb des Vertrags. Wenn das Framework stattdessen zusätzliche Übersetzungen verwenden soll, setzen Sie den booleschen Schlüssel CFBundleAllowMixedLocalizations der App auf YES und testen Sie diese Konfiguration ausdrücklich. Lokalisierung im Swift-Paketmanager.

Wenn Sie diesen Schlüssel aktivieren, wird weder eine Übersetzung noch ein Standardwert für fehlende Nachrichten bereitgestellt. Diese Aufgaben bleiben den Ressourcendateien und den Suchaufrufen überlassen. Ebenso definiert ein für Ihren eigenen Aufruf angegebener Standardwert das Ergebnis dieses Aufrufs bei einer fehlgeschlagenen Suche; er legt keinen universellen englischen Fallback für jedes Systemsteuerelement oder jede Framework-Nachricht fest.

Verwenden Sie das Argument locale dieses String-Initializers nicht als Umschalter für die Übersetzungssprache. Apple dokumentiert ausdrücklich, dass es die Lokalisierung interpolierter Werte wie Zahlen steuert und die für die Suche nach dem lokalisierten String verwendete Locale nicht ändert. Verwenden Sie für diese Aufgabe die unterstützte Sprachkonfiguration und die entsprechenden Tests. String.init(localized:defaultValue:table:bundle:locale:comment:).

Fehlende Sprachen getrennt von fehlenden Nachrichten testen

Führen Sie Lokalisierungstests über Produkt > Schema > Schema bearbeiten aus. Wählen Sie Run, öffnen Sie Optionen, wählen Sie eine App-Sprache, schließen Sie den Dialog und starten Sie die Mac-App erneut. Apple dokumentiert diesen Ablauf, um Lokalisierungen zu testen, ohne die Einstellungen des gesamten Systems zu ändern. Lassen Sie die Regionseinstellung unverändert, wenn Sie ein reines Sprachproblem untersuchen. Lokalisierungen beim Ausführen Ihrer App testen.

Verwenden Sie die folgende Testmatrix als projektspezifische Diagnosehilfe. Verwenden Sie für Prüfungen der Bundle-Zuordnung vorübergehend unterschiedliche englische Testmarker wie App translation sample und Framework translation sample in einer unterstützten Lokalisierung, die nicht der Quellsprache entspricht. Dies sind Diagnoseinhalte, keine Übersetzungen oder Sprachkennungen.

  1. Quellverhalten überprüfen. Führen Sie die App mit ausgewähltem Englisch aus. Erwartet werden Workspace und Panel ready.
  2. Weiterleitung übersetzter Ressourcen überprüfen. Wählen Sie die unterstützte Testlokalisierung aus. Erwartet werden die unterschiedlichen Marker aus den beiden zuständigen Katalogen.
  3. Standardwert bei fehlender Nachricht überprüfen. Ändern Sie in einem Einweg-Test die Suchanfrage des Frameworks zu panel.ready.probe, lassen Sie defaultValue unverändert und sorgen Sie dafür, dass dieser Testeintrag nicht im Testkatalog enthalten ist. Bei fehlgeschlagener Suche wird Panel ready erwartet. Stellen Sie danach den ursprünglichen Schlüssel wieder her.
  4. Auswahl bei nicht unterstützter Sprache überprüfen. Testen Sie eine Liste bevorzugter Sprachen, in der keine unterstützte Sprache enthalten ist. Prüfen Sie, ob beide zuständigen Bundles ihre englischen Entwicklungsressourcen verwenden. Testen Sie auch eine nicht unterstützte Sprache als erste Präferenz, gefolgt von einer unterstützten, da hier ein anderes Ergebnis zu erwarten ist.
  5. Gemischte Lokalisierung überprüfen, falls aktiviert. Testen Sie eine Sprache, die nur im Framework verfügbar ist. Halten Sie das erwartete Verhalten fest und prüfen Sie die Einstellung für gemischte Lokalisierungen der App dagegen.

Die dritte Prüfung isoliert eine fehlgeschlagene Suche; die vierte isoliert den Sprachabgleich. Wenn Sie nur eine nicht übersetzte Katalogzeile testen, können die Bedingungen schwer zu unterscheiden sein, weil die resultierende Ressourcenkonfiguration auch die Suche beeinflusst. Das erwartete Ergebnis für einen fehlenden Schlüssel stammt vom Swift-Initializer; die Auswahl anhand bevorzugter Sprachen richtet sich nach den Bundle-Regeln. String.init(localized:defaultValue:table:bundle:locale:comment:), Auf die Inhalte eines Bundles zugreifen.

Wenn die Ergebnisse abweichen, überprüfen Sie das tatsächlich zuständige Bundle. developmentLocalization meldet den Wert für CFBundleDevelopmentRegion; preferredLocalizations meldet verfügbare Lokalisierungen in der Reihenfolge der Sprachpräferenzen. Vergleichen Sie diese Eigenschaften für Bundle.main und PanelText.resourceBundle. Sie helfen bei der Diagnose der Konfiguration, aber die angezeigten Nachrichten sind weiterhin die entscheidende Prüfung jeder Suche. developmentLocalization, preferredLocalizations.

Ein nützlicher Fallback-Vertrag macht jedes Ergebnis nachvollziehbar: Die ausgewählte Sprache ergibt sich aus dem Bundle-Sprachabgleich, die Nachricht stammt aus der zuständigen Tabelle, und eine fehlgeschlagene Suche gibt einen expliziten, gut lesbaren Standardwert zurück. Wenden Sie diese Regeln auf jede App- und Framework-Nachricht an, für die Sie zuständig sind, und überprüfen Sie sie gemeinsam in dem Desktopfenster, in dem Nutzer ihnen begegnen.

Quellen