För att samla återkommande meddelanden i en Mac-app ger du varje gemensam betydelse en lokaliseringsnyckel och en Swift-definition och använder den sedan överallt där meddelandet visas. Håll dess argument, pluralregler och översättarkontext samlade; det räcker inte att den engelska texten är densamma.

Den här genomgången riktar sig till en Mac-utvecklare som underhåller en sammanfattning av markeringen i både SwiftUI och ett Interface Builder-fönster. Den konkreta uppgiften är att visa meddelanden som 2 items selected in Batch A. på ett enhetligt sätt, utan att underhålla separata meningsmallar i en XIB och i Swift-kod. Alla antal och etiketter nedan är syntetiska.

Avgör vilka återkommande meddelanden som hör ihop

Innan du redigerar kataloger definierar du meddelandekontraktet: vad meningen betyder, vilka värden den tar emot och var den visas. I det här exemplet ser kontraktet ut så här:

Kontraktselement Definition
Betydelse Antal objekt som för närvarande är markerade i en namngiven samling
Nyckel selection.summary
Tabell Localizable
Paket Appens huvudpaket
Argument 1 Ett markeringsantal av typen Int64
Argument 2 En bokstavlig samlingsetikett, till exempel Batch A
Variation Pluralform som avgörs av markeringsantalet
Kontext En fullständig statusmening som visas i SwiftUI och i en AppKit-etikett

Använd det här kontraktet för att avgöra om meddelanden kan samlas. Två förekomster kan dela meddelande när deras betydelse, argument och grammatiska funktion stämmer överens. Antalet markerade objekt och det totala antalet objekt bör hållas isär även om båda för närvarande visas som 2 items. En knapp med namnet Select behöver också ett eget meddelande eftersom den uttrycker en handling.

Apple stöder uttryckliga nycklar med separata standardvärden särskilt för att skilja mellan texter. Det gör att du kan namnge ett meddelande efter dess syfte och samtidigt behålla läsbar källtext. En kommentar ger kontext; nyckeln identifierar posten. String’s explicit-key initializer.

Behandla även tabellen och paketet som delar av uppslagskontraktet. En nyckel i en annan tabell är ett separat uppslagsmål. Apple beskriver hur tabell väljs med parametern table eller tableName och rekommenderar att lokaliserbar text skickas vidare genom LocalizedStringResource. I det här exemplet ligger hjälpfunktionen och katalogen i appens mål och använder huvudpaketet. Preparing your app’s text for translation.

Förstå gränsen mellan extrahering och formatering vid körning

Extrahering sker när Xcode bygger appen: verktyget hittar lokaliserbara uttryck som stöds och synkroniserar deras källtext med kataloger. Lokalisering vid körning sker när appen slår upp ett meddelande och ersätter platshållarna med faktiska värden. Att hitta en post i en katalog visar att extraheringen hittade den; det bevisar inte att varje kontroll visar den korrekt.

Interface Builder har ett eget arbetsflöde för resurser. Apple beskriver kataloger som hör ihop med ett storyboard eller en XIB, medan Swift-källkod normalt bidrar till tabellen Localizable. Det dokumenterade arbetsflödet beskriver ingen aliasfunktion mellan resurser som slår ihop meddelanden enbart för att deras synliga text är densamma. Ett praktiskt och stödd tillvägagångssätt är att tilldela det delade lokaliserade meddelandet från Swift när gränssnittet har lästs in. Det här är en implementation som bygger på Apples dokumenterade API:er. Discover String Catalogs.

SwiftUI skiljer också mellan en bokstavlig lokaliseringsnyckel och ett vanligt strängvärde. Till exempel begär Text("Summary") lokalisering, medan en String-variabel visar sitt värde utan uppslagningen. Om en sträng vid körning omsluts i LocalizedStringKey begärs en uppslagning, men det ska inte förväxlas med ett bevis på att extraheringen hittade möjliga värden. LocalizedStringKey.

Lösningen nedan håller därför den extraherbara bokstavliga texten i en enda hjälpfunktion. Den skickar med antal och etikett i en LocalizedStringResource och slår sedan upp resursen där en färdig sträng behövs. Apple beskriver den här resurstypen som ett sätt att skjuta upp lokaliseringsuppslagningen. Den finns i macOS 13 och senare, som är den lägsta målversionen i det här exemplet. LocalizedStringResource.

Definiera det delade meddelandet en gång i Swift

Skapa ett macOS-approjekt med SwiftUI, ange macOS 13 eller senare som distributionsmål och lägg till Localizable.xcstrings i appens mål. Enligt den aktuella Xcode-dokumentationen skapar du filen genom att välja File > New > File from Template, söka efter string och välja String Catalog. Localizing and varying text with a string catalog.

Ersätt appens genererade startpunkt med hela programmet nedan. Ta bort den ursprungliga startpunkten så att målet bara innehåller en @main-deklaration. SwiftUI-fönstret körs oberoende av en XIB; den inkluderade AppKit-underklassen används när en befintlig Interface Builder-etikett anpassas i nästa avsnitt.

import AppKit
import Foundation
import SwiftUI

enum SelectionMessages {
    static func summary(
        count: Int64,
        collectionLabel: String
    ) -> LocalizedStringResource {
        LocalizedStringResource(
            "selection.summary",
            defaultValue: "\(count) items selected in \(collectionLabel).",
            table: "Localizable",
            comment: "Complete selection-status sentence used in SwiftUI and an AppKit label. Argument 1 is the selected-item count; argument 2 is a literal collection label. Pluralize by argument 1."
        )
    }
}

struct SelectionDemoView: View {
    private let counts: [Int64] = [0, 1, 2]
    private let collectionLabel = "Batch A"

    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            ForEach(counts, id: \.self) { count in
                Text(String(localized: SelectionMessages.summary(
                    count: count,
                    collectionLabel: collectionLabel
                )))
            }
        }
        .padding(24)
        .frame(minWidth: 360, minHeight: 160)
    }
}

final class SelectionSummaryField: NSTextField {
    override func awakeFromNib() {
        super.awakeFromNib()
        // Synthetic initial state for the Interface Builder example.
        updateSelection(count: 2, collectionLabel: "Batch A")
    }

    func updateSelection(count: Int64, collectionLabel: String) {
        stringValue = String(localized: SelectionMessages.summary(
            count: count,
            collectionLabel: collectionLabel
        ))
    }
}

@main
struct SelectionDemoApp: App {
    var body: some Scene {
        WindowGroup {
            SelectionDemoView()
        }
    }
}

Den statiska nyckeln identifierar meddelandet; det interpolerade standardvärdet innehåller källmeningen och argumenten som används vid körning. Initieraren tar även emot tabell, paket och en kommentar till översättaren. Standardpaketet är huvudpaketet. LocalizedStringResource’s explicit-key initializer.

Etiketten Batch A är exempeltext som infogas i meningen. Den är inte en annan meddelandenyckel. Den här skillnaden håller samlingsetiketter åtskilda från gränssnittstexten runt dem. Om din verkliga app använder fördefinierade, översatta samlingsnamn lokaliserar du namnen med deras egna definitioner innan du skickar dem hit.

Bygg inte ihop meningen av separat lokaliserade delar, som ett antal, items selected in och en etikett. Om hela meningen ingår i kontraktet får lokaliserarna en enda enhet vars formulering och argumentordning kan justeras tillsammans.

Låt Interface Builder-etiketten använda samma definition

Använd en statisk NSTextField-etikett för sammanfattningen i en befintlig XIB eller ett storyboard. Ange SelectionSummaryField som anpassad klass och appens mål som modul. Rensa bort den gamla meningen från designens strängvärde, eftersom den här etiketten nu får sin text från den delade Swift-definitionen.

När gränssnittet läses in anropar underklassen super.awakeFromNib() och tilldelar den syntetiska inledande sammanfattningen. Apple beskriver awakeFromNib() som kroken som körs efter inläsningen, när outlet- och åtgärdsanslutningar har upprättats, och kräver att superklassens implementation anropas. awakeFromNib().

I det verkliga fönstret anropar du updateSelection(count:collectionLabel:) på den anslutna etiketten varje gång markeringen ändras, med samma modellvärden som skickas till SwiftUI. Ersätt den syntetiska inledande tilldelningen med fönstrets faktiska initieringsflöde. En engångstilldelning vid inläsning kan inte följa senare ändringar av markeringen.

AppKit-adaptern anger stringValue, ett dokumenterat sätt att tilldela texten för en NSTextField. Layouten ligger därmed kvar i Interface Builder, medan Swift får ansvaret för meddelandet. Se till att etiketten har tillräcklig bredd eller kan radbryta så att hela meningen får plats. NSTextField.

Låt orelaterad statisk gränssnittstext ligga kvar i sitt befintliga resursflöde. Den här migreringen gäller ett delat meddelande; det behövs inte att varje etikett flyttas till Swift. Kontrollera också att en bindning eller en annan kontroller inte senare ersätter sammanfattningen med den gamla formuleringen.

Konfigurera pluralformer utan att ändra argumenten

Välj Product > Build, öppna Localizable.xcstrings och leta reda på selection.summary. Ctrl-klicka på posten, välj Vary by Plural och välj det numeriska antalargumentet om Xcode frågar vilket argument som ska varieras. Apple beskriver det här arbetsflödet för interpolerade meddelanden. Localizing and varying text with a string catalog.

För den engelska källspråksversionen ska singularformen ge 1 item selected in Batch A. och den andra formen meningar som 2 items selected in Batch A.. Behåll båda argumentplatshållarna som Xcode tillhandahåller. Redigera den omgivande formuleringen i stället för att ersätta en platshållare med ett exempelantal eller en exempel-etikett.

Syntetisk indata Förväntat engelskt resultat efter pluralinställningen
Antal 0, etikett Batch A 0 items selected in Batch A.
Antal 1, etikett Batch A 1 item selected in Batch A.
Antal 2, etikett Batch A 2 items selected in Batch A.
Antal 2, etikett Batch B 2 items selected in Batch B.

Det här är illustrativa acceptansfall, inte uppmätta resultat. Innan pluralinställningen görs innehåller källmeningens standardtext fortfarande items för alla antal. Extraheringen bevarar det interpolerade meddelandet; den rättar inte grammatiken på egen hand.

Den ändrade etiketten på sista raden är användbar vid felsökning: om visningen fortfarande är Batch A kan ett exempelvärde ha hårdkodats i en form eller så skickar anroparen in inaktuellt tillstånd. Om etiketten försvinner granskar du formens platshållare innan du ändrar hjälpfunktionens signatur.

Verifiera varje visningsväg innan du tar bort gamla poster

Testa SwiftUI-fönstret och det faktiska Interface Builder-fönstret var för sig. Använd samma antal och etiketter i båda. Kontrollera den inledande inläsningen, en senare ändring av markeringen och en längre syntetisk etikett, till exempel Batch A Extended. Bekräfta att formuleringen överensstämmer och att meningen fortfarande är lättläst.

För varje lokaliseringsspråk väljer du Product > Scheme > Edit Scheme, väljer Run, öppnar Options och anger App Language. Kör appen igen och granska båda visningsvägarna. Apple beskriver de här schema-inställningarna för att testa lokaliseringar utan att ändra språket för hela systemet. Testing localizations when running your app.

Använd följande ordning när resultatet är fel:

  1. Post saknas: Kontrollera att katalogen tillhör appens mål och bygg det relevanta schemat. Kontrollera att Use Compiler to Extract Swift Strings är aktiverat.
  2. Källtexten visas oväntat: Kontrollera nyckeln, tabellen, paketet och översättningen innan du betraktar den läsbara reservtexten som ett lyckat resultat.
  3. Bara Interface Builder-etiketten skiljer sig: Kontrollera dess anpassade klass, modul, inläsningsflöde och efterföljande tilldelningar.
  4. Bara senare värden skiljer sig: Kontrollera att båda gränssnitten får uppdaterade modellvärden.
  5. Pluralformen är fel: Kontrollera vilket numeriskt argument som valts för variationen och alla tillämpliga former.

Apple beskriver kompilatorinställningen för extrahering och förklarar att översatta poster som inte längre hittas i källan kan markeras som Stale. Det är en signal om att städa upp, inte ett bevis på att resursen inte används. Granska den gamla gränssnittstilldelningen och Swift-anroparna innan du tar bort en post och dess översättningar. Discover String Catalogs.

Konsolideringen är klar när båda gränssnitten får samma kontextuella meddelande från en enda definition, argumenten finns kvar och inga anropare längre använder de gamla meningsmallarna. Förvara kontraktet och acceptansfallen intill hjälpfunktionen så att framtida ändringar av formuleringen bevarar betydelsen som delas av båda fönstren.

Källor