For a reusable macOS label, accept localizable messages as LocalizedStringResource, keep a separate verbatim: path for literal text, and preserve interpolation until localization. This tutorial builds that contract for a desktop item counter and checks extraction separately from display.
What should the reusable text API promise?
Suppose a Mac app needs a compact summary showing an item count and a synthetic collection label such as Sample Set. The reusable component must distinguish three inputs: an app-authored message that needs translation, an existing SwiftUI localization key, and text that must appear exactly as supplied.
Apple recommends LocalizedStringResource for representing and passing localizable strings through custom views and other Swift code. Its current guidance demonstrates custom view properties of that type being displayed with Text. Use it as the primary message contract when designing a new component. Preparing your app’s text for translation.
The following decision table is an API design derived from those documented behaviors. The explicit argument labels make the caller’s intention reviewable without relying on two competing, unlabeled string overloads.
| Caller’s intention | Proposed entry point | Rendering path |
|---|---|---|
| Pass a translatable message with optional context | SummaryLabel(localized:) |
Text(resource) |
| Pass a SwiftUI key, including SwiftUI interpolation | SummaryLabel(key:) |
Text(key, tableName:bundle:) |
| Display an exact label | SummaryLabel(verbatim:) |
Text(verbatim:) |
| Supply text already composed by the caller | SummaryLabel(text:) |
Store the supplied Text |
Keep LocalizedStringKey available when the component specifically needs SwiftUI’s key-based behavior. Apple documents that a string literal passed directly to Text creates a localization key, while a String variable normally takes a path that bypasses localization. A wrapper accepting only String therefore changes the meaning of otherwise similar-looking calls. LocalizedStringKey.
Build a complete component with explicit entry points
Use a macOS SwiftUI app project in Xcode 26 or later, with its deployment target set to macOS 26 for this example. Replace the generated app and content-view declarations with the complete program below; keep only one @main declaration in the target. All labels and counts are synthetic. The code was inspected against the documented APIs, but was not compiled here because this environment has no Swift compiler.
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()
}
}
}
The resource initializer deliberately separates the stable key summary.item-count from its default message, \(itemCount) items. Apple documents a static key, a localizable default value, a table, a locale, a bundle, and a translator comment for this initializer. Its default value is used when the key is absent from the specified table. LocalizedStringResource initializer.
The verbatim: initializer establishes an equally clear promise: display the supplied string without localization. It works for a literal argument as well as a variable. Use this path for the synthetic collection label when its exact spelling is the content to display. Text.init(verbatim:).
The text: entry point lets the caller construct localized or styled text before handing it over. This component adds shared typography and spacing; the caller still chooses the message semantics. Passing Text is an escape hatch, so a component that must enforce a narrower contract can omit that initializer.
How do extraction and runtime formatting differ?
Treat localization as two separate checks. During a build, Xcode discovers localizable source expressions and updates the String Catalog. During execution, the app resolves the message and substitutes its arguments. Apple’s String Catalog session explicitly describes recognizing literals used to instantiate LocalizedStringResource at a custom-view call site. It also identifies the Use Compiler to Extract Swift Strings build setting. Discover String Catalogs.
Interpolation must reach a localization-aware type before becoming an ordinary completed string. Apple explains that SwiftUI’s interpolation builder keeps the format key and argument values separate, allowing localization before substitution. Converting an already interpolated String into a key cannot reconstruct those original typed arguments. What’s New in Swift.
This diagnostic fragment shows the distinction; it is not a second app. All values used in the fragment are defined locally.
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)
Creating LocalizedStringKey from a variable is a documented way to request runtime lookup. It is useful when the variable intentionally contains a known key. It does not establish that Xcode extracted every possible value of that variable into a catalog. Maintain intentional dynamic keys explicitly, or use literal resource declarations for the finite messages your app owns. The lookup behavior and fallback are documented in Text.init(_:tableName:bundle:comment:).
Preserve the whole message, including its punctuation and placeholder positions, in one localizable expression. For this example, keep Collection: \(collectionLabel) together. Building a completed message from separately resolved fragments would leave the caller responsible for their order and spacing.
Add the catalog and configure plural behavior
First, add a String Catalog through File > New > File from Template, select String Catalog, and name it Localizable. Then choose Product > Build. Apple documents that building populates the catalog from localizable APIs and updates it as source messages change. Localizing and varying text with a string catalog.
For the sample, inspect the summary.item-count entry and its interpolated integer. Control-click the entry and choose Vary by Plural. In the English source localization, set One to %lld item and Other to %lld items, retaining the integer placeholder Xcode supplies. Add the required forms when adding other localizations; plural categories depend on the language. Localizing and varying text with a string catalog.
Interpolation supplies the count, but it does not by itself author the singular wording. Apple’s code-along demonstrates adding plural variants in the catalog and the system selecting the matching variant at runtime. This is why a successful build and an extracted message are only part of the verification. Code-along: Explore localization with Xcode.
Use this small acceptance matrix after configuring the source plural forms. These are expected results for the illustrative program, not reported execution results.
| Check | Expected result |
|---|---|
Build and inspect summary.item-count |
One message entry with an integer substitution |
Set the count to 0 |
0 items |
Set the count to 1 |
1 item |
Set the count to 2 |
2 items |
| Inspect the exact-label row | Sample Set remains literal |
| Inspect the collection message | The label is an argument within the complete message |
Diagnose missing entries separately from incorrect display
If a message is missing from the catalog, inspect its source type first. A String parameter may have erased the localizable expression before it reached the component. Check the catalog’s target, build the scheme containing that call site, and verify Use Compiler to Extract Swift Strings. Apple describes extraction as operating on the current scheme and platform, so inspect the configuration that actually compiles the message. Discover String Catalogs.
If an entry exists but the app displays a key or fallback text, inspect lookup configuration. The key-based Text initializer searches the specified table and bundle; omitted values select the default table and main bundle. An absent key or table produces the key’s string representation. A readable fallback can therefore conceal an incomplete lookup. Text.init(_:tableName:bundle:comment:).
When moving the component into a framework or package, decide where the messages belong. Component-owned messages need the component’s resource bundle; caller-owned resources should retain their supplied lookup information. Apple’s current guidance supports passing #bundle to localization APIs to identify the containing target’s bundle. Named tables likewise need matching catalogs. Preparing your app’s text for translation.
Finally, do not assume every interpolation accepted by SwiftUI also belongs in a Foundation resource. LocalizedStringKey has its own interpolation methods, including format-style and formatter variants. Choose the message type before composing the expression, and consult its supported interpolation operations when an argument fails to compile. LocalizedStringKey.StringInterpolation.
A reusable text component is ready when callers can express their intent clearly and verify it at both stages: the build produces the intended catalog message, and the running Mac app displays the correct substitution, plural form, or exact label. Keep those checks alongside the API contract whenever the component changes.
Sources
- Preparing your app’s text for translation — Apple
- LocalizedStringKey — Apple
- init(_:defaultValue:table:locale:bundle:comment:) — Apple
- init(verbatim:) — Apple
- Discover String Catalogs — Apple
- What’s New in Swift — Apple
- init(_:tableName:bundle:comment:) — Apple
- Localizing and varying text with a string catalog — Apple
- Code-along: Explore localization with Xcode — Apple
- LocalizedStringKey.StringInterpolation — Apple