To remove unused localization keys in Xcode, treat Stale entries as review candidates. Check runtime key construction before deleting, preserve required dynamic messages as manually managed entries, and test the affected Mac app screens after cleanup.
This guide is for a developer maintaining a SwiftUI Mac app with String Catalogs. The task is one catalog cleanup: identify messages that can be removed while preserving labels and count messages the app still displays. The decision aid below separates extraction evidence from runtime behavior.
What does a stale localization key actually mean?
Xcode discovers localizable strings during builds. Apple explains that extraction covers the current scheme and platform. When a previously extracted string disappears, Xcode can remove an untranslated entry automatically; an entry with translations remains and receives the Stale state. That state means Xcode no longer finds the string in code. Apple explicitly makes deletion conditional on confirming that the string is no longer needed. Discover String Catalogs
The practical consequence is that a missing source reference starts an investigation. An exact search for panel.compact can find a literal reference, but it cannot establish whether code constructs that key by joining panel. with a selected mode. Review both the candidate key and the code that produces lookup keys.
Use this proposed review table for each candidate. It is a decision aid derived from the documented extraction boundary, not an additional Xcode feature.
| Evidence found during review | Decision | Next action |
|---|---|---|
| A removed screen was its only consumer; no runtime key producer remains | Delete candidate | Remove the entry, then rebuild and check the change |
| Runtime code can still produce this exact key | Keep | Preserve it through manual management or replace the lookup with explicit localizable references |
| The key is still referenced in another target or build condition | Keep | Build and inspect the relevant configuration |
| A similarly named key exists in another table | Investigate separately | Confirm the table and resource bundle used by each lookup |
| No consumer is found, but the key-producing code is unclear | Defer | Trace the producer before making a deletion decision |
How do you establish a useful baseline in Xcode?
First, save a recoverable copy of the catalog or commit its current state. Then open the app project and select the Mac scheme you intend to review. Record which targets and conditional configurations can supply messages to the affected interface; this keeps the review tied to an explicit scope.
Select the catalog in the Project navigator and choose Product > Build. Apple documents that builds populate and update String Catalogs from localizable source text. With the catalog selected, choose Editor > Assistant, then select a key to inspect its source location. The catalog also provides navigation back to source. Localizing and varying text with a string catalog
If expected Swift strings are missing, check that the target’s Use Compiler to Extract Swift Strings build setting is enabled. Apple documents this prerequisite for compiler extraction. A catalog populated with incomplete extraction is a poor baseline for deciding what to remove. Localize your SwiftUI app
For each candidate, record the key, catalog, suspected consumer, and decision. Search for the exact key first, then its stable prefix and relevant localization wrappers. Inspect model values and bundled lookup data that feed those wrappers. Repeat the relevant builds when another target or compile condition changes which source is included.
Do not merge decisions merely because two entries display the same text. Apple’s localization APIs accept table names, and resources outside the main app can require a different bundle. Trace the lookup’s table and bundle before treating another entry as a replacement. Preparing your app’s text for translation
How do you preserve dynamically referenced messages?
Apple documents manual management specifically for strings whose keys are constructed dynamically. Select the required entry and use its inspector to make it manually managed. Such entries are not updated or removed by Xcode’s post-build localization synchronization. For a missing dynamic entry, Apple also demonstrates adding a manual string with the catalog’s plus button. Discover String Catalogs
Manual management requires an explicit maintenance rule. Record the valid keys beside the code or data that selects them, and explain their use in catalog comments. When a supported mode is removed later, review its manual entry too; automatic extraction will not perform that cleanup for you.
Check whether SwiftUI actually performs a lookup
A String variable passed to Text is displayed without localization. To use a variable as a lookup key, Apple documents constructing a LocalizedStringKey from it. A literal passed directly to Text takes the localizable path automatically. These overloads can produce identical source-language output while behaving differently. LocalizedStringKey
The following complete demonstration app keeps a runtime-selected key separate from an interpolated count. In a disposable SwiftUI Mac app project using Xcode 15 or later and a macOS 13 deployment target, replace the generated app declaration with this code. Keep only one @main declaration. It was inspected against the documented APIs; it was not compiled here because a Swift toolchain was unavailable.
import SwiftUI
@main
struct CatalogReviewDemoApp: App {
var body: some Scene {
WindowGroup {
CatalogReviewView()
}
}
}
struct CatalogReviewView: View {
@State private var compact = true
@State private var itemCount = 2
private var selectedKey: String {
"panel." + (compact ? "compact" : "expanded")
}
var body: some View {
VStack(alignment: .leading, spacing: 12) {
Toggle("Compact layout", isOn: $compact)
Text(LocalizedStringKey(selectedKey))
Text("\(itemCount) items",
comment: "Number of items in the sample panel.")
Button("Add item") {
itemCount += 1
}
}
.padding()
.frame(minWidth: 320)
}
}
Add Localizable.xcstrings to the app target. Add manually managed entries for panel.compact and panel.expanded, with source values Compact panel and Expanded panel. Build the project to extract the literal messages. The explicit LocalizedStringKey conversion requests a runtime lookup; it does not supply the extractor with a literal declaration of every possible constructed key.
For a small, finite set of choices, consider replacing key construction with explicit localizable literals in each branch. For custom interfaces that pass messages between components, Apple recommends LocalizedStringResource, which carries localization information such as a table, comment, and default value. Choose the representation that makes the complete supported message set easiest to inspect. Preparing your app’s text for translation
Why are changing counts different from changing keys?
In the demonstration, selecting a layout changes which message key the app looks up. Changing itemCount supplies a different argument to one localizable message. SwiftUI interpolation converts embedded values into format placeholders for localization; runtime rendering supplies their values. Localize your SwiftUI app
You therefore do not need separate keys for the rendered labels 2 items, 3 items, and 4 items. Build the example, find the extracted count message, Control-click its key, and choose Vary by Plural. For an English source localization, enter %lld item for One and %lld items for Other, using the integer placeholder shown by Xcode. Apple documents this count-message workflow and the addition of appropriate plural variants for other supported localizations. Localizing and varying text with a string catalog
During cleanup, inspect a count message as a complete resource, including its variations. The absence of a rendered phrase such as 4 items from source code says nothing about whether the underlying interpolated message is used. Likewise, keeping a count template does not preserve an unrelated dynamic key simply because both messages appear in the same panel.
Which entries should a cleanup actually remove?
Consider an illustrative review of 24 candidates. These counts describe a synthetic example, not a measured project result.
| Review result | Entries | Action |
|---|---|---|
| Only consumers belonged to a removed panel | 9 | Delete after confirming the panel and its lookup data are gone |
| Keys remain reachable through runtime selection | 8 | Keep and document manual management |
| References belong to another target or conditional build | 5 | Keep and verify that build scope |
| Key origin remains unresolved | 2 | Retain pending investigation |
| Total reviewed | 24 | 9 deletion candidates |
This calculation makes the cleanup decision auditable: 24 reviewed entries do not imply 24 deletions. For each of the nine deletion candidates, retain a short reason identifying the removed consumer and the runtime lookup paths checked. For the eight dynamic entries, record the permitted key set so the next review can repeat the decision.
Remove only the confirmed candidates, then inspect the catalog diff. Check that the change contains the intended keys and their associated translations, without unrelated plural or device variants disappearing. Rebuild the relevant configurations. If a deleted key returns through extraction, revisit its consumer before deleting it again.
How do you verify the retained messages on your Mac?
Use Product > Scheme > Edit Scheme, select Run, and open Options. Choose an existing supported localization from App Language, close the editor, and run the Mac destination. Apple documents these scheme controls for testing localized builds. Repeat for the localizations your app supports. Testing localizations when running your app
Exercise every permitted dynamic selection, including both layouts in the demonstration. Confirm that the app displays the expected catalog values rather than the identifiers. For count messages, check synthetic values such as 0, 1, 2, and 12, and verify additional plural categories where your supported localizations require them. Changing the sample’s initial count is sufficient to reach those test values.
When a retained message displays incorrectly, inspect the exact key, table, bundle, and Text initializer before changing the catalog. Apple’s newer generated-symbol workflow can also make manual resources easier to reference: Xcode 26 introduced String Catalog symbol generation, enabled for existing projects through Generate String Catalog Symbols. This is an optional refactoring path, not evidence that an unreferenced manual resource is unused. Code-along: Explore localization with Xcode
Finish with a small, explainable deletion set and a documented list of retained dynamic messages. The useful result is a catalog whose remaining entries have identifiable consumers, with runtime-selected keys and formatted count messages still working in the Mac interface.
Sources
- Discover String Catalogs — Apple Developer
- Localizing and varying text with a string catalog — Apple Developer
- Preparing your app’s text for translation — Apple Developer
- LocalizedStringKey — Apple Developer
- Localize your SwiftUI app — Apple Developer
- Testing localizations when running your app — Apple Developer
- Code-along: Explore localization with Xcode — Apple Developer