Xcode 15’s String Catalogs revolutionize iOS localization by replacing fragmented .strings files with a unified, automated approach that dramatically reduces localization complexity while improving translation management and developer productivity.

What are String Catalogs?
Apple’s introduction of String Catalogs in Xcode 15 represents the most significant advancement in iOS localization tooling in over a decade. This modern system automatically extracts strings from your code during compilation, manages all translations in a single JSON-based file, and provides visual progress tracking—eliminating the manual string synchronization headaches that have plagued iOS developers for years. The technology works with all iOS versions and integrates seamlessly with existing localization workflows, making it an essential upgrade for any team serious about international app distribution.
For teams currently managing dozens of separate .strings files across multiple languages, String Catalogs offer immediate relief from sync issues, missing translations, and broken localization references that commonly surface during app store submissions. The unified format also enables more sophisticated features like automatic pluralization, device-specific variants, and built-in translator context—capabilities that previously required complex .stringsdict configurations.
Understanding the String Catalog revolution
Traditional iOS localization required developers to maintain separate .strings files for each language in respective .lproj directories, plus additional .stringsdict files for pluralization rules. This fragmented approach created constant synchronization challenges between source code and translation files, often resulting in missing strings, outdated translations, and broken localization references discovered only during testing or App Store review.
String Catalogs solve these fundamental problems through automatic string extraction. When you build your project with the “Use Compiler to Extract Swift Strings” build setting enabled, Xcode’s compiler technology identifies every localizable string in your codebase and automatically populates the String Catalog. New strings appear immediately, changed strings are flagged for translator review, and unused strings are marked as stale—creating a self-maintaining localization system.
The .xcstrings
file format uses structured JSON to store all languages and translations in a single file. This unified approach provides several critical advantages: better version control with meaningful diffs, visual progress tracking for translation completeness, built-in pluralization without separate files, and elimination of the sync drift that plagued traditional workflows.
Setting up your first String Catalog requires just a few steps. Create a new String Catalog by selecting File → New → File… → iOS → Resource → String Catalog. Name it Localizable.xcstrings
for the default string table, or use custom names like Settings.xcstrings
for feature-specific tables. Ensure the build setting “Use Compiler to Extract Swift Strings” is set to YES, then build your project to automatically populate the catalog with discovered strings.
The String Catalog’s JSON structure organizes strings with extraction states, translation statuses, and metadata. Each string entry contains extractionState
(manual or automatic), optional translator comments, and localizations
objects for each target language. The system tracks translation states—NEW for untranslated strings, TRANSLATED for completed translations, NEEDS REVIEW for strings whose source values have changed, and STALE for strings no longer found in code but containing existing translations.
Modern localization API methods
iOS developers now have three primary approaches for implementing localized strings, each optimized for different use cases and iOS version requirements.
NSLocalizedString remains the traditional foundation for iOS localization, providing maximum compatibility across all iOS versions since iOS 2.0. This Objective-C heritage API requires explicit function calls and comments but offers complete control over bundle, table, and value parameters. The syntax NSLocalizedString("welcome_message", comment: "Greeting shown to users")
is verbose but reliable, with automatic fallback behavior that returns the key if translations are missing—preventing blank user interfaces.
The mature NSLocalizedString approach excels in legacy codebases, mixed Objective-C/Swift projects, and scenarios requiring precise bundle or table selection. Its genstrings command-line tool compatibility makes it valuable for teams with established extraction workflows, though the manual maintenance requirements and error-prone key management represent significant drawbacks compared to modern alternatives.
String(localized:) introduces Swift-native localization available in iOS 15+, offering cleaner syntax while maintaining String Catalog compatibility. This approach uses String(localized: "Welcome to the app!")
for basic cases and supports translator comments, default values, and custom tables through additional parameters. The API naturally handles Swift string interpolation and provides built-in default value fallbacks, making it more robust than NSLocalizedString for handling missing translations.
The String(localized:) method represents the sweet spot for most iOS development teams—modern Swift syntax without the iOS 16+ requirement of LocalizedStringResource. However, developers should note that the locale parameter doesn’t function as expected, and using English text as keys creates translation fragility when source strings change.
LocalizedStringResource provides the most advanced localization for iOS 16+ applications, offering type-safe localization resources designed for SwiftUI and App Intents. This approach creates reusable localization objects: LocalizedStringResource("user_profile_title", defaultValue: "User Profile", table: "UI", comment: "Title for user profile screen")
. These resources bundle key, default value, comment, and table information into strongly typed objects that work seamlessly across processes.
LocalizedStringResource becomes essential for App Intents implementation, where it’s required for intent titles and descriptions. The type safety and metadata bundling make it ideal for complex SwiftUI applications with multiple string tables, though the iOS 16+ requirement and learning curve limit adoption in projects supporting older iOS versions.
Comprehensive localization method comparison
Each localization approach offers distinct advantages depending on your project requirements and constraints.
Performance characteristics vary minimally between methods. NSLocalizedString provides the fastest runtime performance with direct bundle lookups averaging ~0.001ms per string. String(localized:) performs similarly to NSLocalizedString, while LocalizedStringResource introduces slight overhead (~0.002ms) due to object creation but remains negligible for practical applications.
String Catalog integration works seamlessly across all three methods. Each approach supports automatic extraction when “Use Compiler to Extract Swift Strings” is enabled, with the compiler identifying localizable strings regardless of which API you use. The visual progress tracking, translation states, and XLIFF export capabilities function identically across all methods.
Version compatibility represents the primary differentiator. NSLocalizedString works on all iOS versions, String(localized:) requires iOS 15+, and LocalizedStringResource needs iOS 16+. This progression reflects Apple’s localization evolution—from Objective-C heritage to Swift-native design to type-safe modern implementation.
Team workflow considerations vary significantly between approaches. NSLocalizedString’s explicit key-value separation works well for teams with dedicated localization staff who prefer traditional CAT tools. String(localized:) suits Swift-first teams prioritizing clean code with automatic extraction. LocalizedStringResource fits SwiftUI-focused teams building modern iOS 16+ applications with complex localization requirements.
The migration path typically follows NSLocalizedString → String(localized:) → LocalizedStringResource as teams modernize codebases and increase minimum iOS version requirements. During transitions, all three methods can coexist within the same project, allowing gradual adoption without disrupting existing functionality.
Best practices for string organization
Effective String Catalog organization starts with strategic table separation. Create separate String Catalogs for logical groupings: Localizable.xcstrings
for core app strings, Settings.xcstrings
for preferences, Onboarding.xcstrings
for first-run experience, and Widgets.xcstrings
for widget-specific content. This modular approach improves translator focus, reduces merge conflicts, and enables feature-specific localization workflows.
Key naming conventions dramatically impact long-term maintainability. Use descriptive, hierarchical keys like Profile.Settings.Title
or Order.Summary.ItemCount
rather than generic terms. Establish team-wide naming standards and document them clearly—consistency becomes critical as string counts grow into hundreds or thousands across multiple languages.
Translation context through comments provides essential information for accurate translations. Use the comment parameter consistently: String(localized: "Save", comment: "Button to save user preferences")
helps translators understand context, usage, and appropriate tone. String Catalogs automatically extract these comments and present them to translators in XLIFF exports.
State management leverages String Catalog’s four translation states effectively. NEW strings require translator attention, TRANSLATED strings are complete, NEEDS REVIEW strings have changed source values requiring translation updates, and STALE strings exist in translations but no longer appear in code. Monitor these states regularly and establish team workflows for addressing each category promptly.
Advanced features like pluralization and device variants enhance user experience significantly. Right-click any string in the String Catalog editor and select “Vary by Plural” to automatically create language-appropriate plural forms. Device variants enable platform-specific strings—”Click” for Mac, “Tap” for iOS—improving interface consistency across Apple’s ecosystem.
The String Catalog’s substitution system handles complex pluralization scenarios involving multiple variables. Use the %#@argumentName@
syntax for sophisticated cases like “%#@birds@ in %#@yards@” where both “birds” and “yards” need independent pluralization rules.
Step-by-step implementation tutorial
Building a localized iOS app with String Catalogs starts with proper project configuration. First, enable the critical build setting “Use Compiler to Extract Swift Strings = YES” in your target’s build settings. This enables automatic string extraction during compilation. Optionally set “Localization Prefers String Catalogs = YES” to ensure XLIFF exports use the modern format.
Create your String Catalog by adding a new file: File → New → File… → iOS → Resource → String Catalog. Name it Localizable.xcstrings
and ensure it’s added to your app target. Build your project (⌘+B) to populate the catalog with any existing localizable strings from your codebase.
Add target languages in your project settings. Select your project in the Navigator, go to the Info tab, and add languages in the Localizations section. Then open your String Catalog and click the “+” button to add the same languages to your catalog.
Implement localized strings using modern APIs. For SwiftUI views, simple Text views are automatically extracted:
struct WelcomeView: View {
var body: some View {
VStack {
Text("Welcome to WWDC!") // Automatically extracted
Text("Get started by exploring")
Button("Continue") {
// Handle action
}
}
}
}
For more complex scenarios, use explicit localization:
struct ProfileView: View {
let userName: String
var body: some View {
VStack {
Text("Hello, \(userName)!") // Interpolation handled automatically
// Explicit localization with comment
Text(String(localized: "Edit Profile",
comment: "Button to edit user profile"))
// iOS 16+ with LocalizedStringResource
let logoutResource = LocalizedStringResource(
"logout_button",
defaultValue: "Log Out",
comment: "Button to sign out of account"
)
Button(action: logout) {
Text(logoutResource)
}
}
}
}
Handle pluralization by using String Catalog’s built-in support. Create strings like:
let itemCount = 5
Text("^[\(itemCount) item](inflect: true)") // Automatic pluralization
The String Catalog automatically creates appropriate plural variations for each language, handling complex rules like Ukrainian’s few/many/other distinctions.
Organize with custom tables for feature separation:
// Create SettingsLocalizable.xcstrings for settings-specific strings
Text(String(localized: "Privacy Settings",
table: "SettingsLocalizable",
comment: "Privacy section header"))
Test your implementation by changing the simulator language (Settings → General → Language & Region) or configuring Xcode schemes with different languages (Edit Scheme → Options → App Language).
Common pitfalls and prevention strategies
The most critical error involves coexistence conflicts between String Catalogs and traditional .strings files. You cannot have both Localizable.xcstrings
and Localizable.strings
in the same target—this generates build errors. Resolve this by migrating completely or using different table names for gradual transitions.
Build setting misconfiguration prevents automatic string extraction. Ensure “Use Compiler to Extract Swift Strings” is enabled and rebuild your project after creating String Catalogs. Strings won’t appear in catalogs until the next build cycle.
Base.lproj migration issues occur when .strings files exist in the Base localization folder. String Catalogs don’t support Base.lproj—move .strings files to language-specific folders (en.lproj, de.lproj) before migration.
Comment concatenation problems happen when identical strings appear in multiple contexts with different comments. String Catalogs merge these comments, but the order can change between builds, causing unnecessary version control noise. Prevent this by using unique keys for different contexts:
// Instead of generic keys
Text("Save") // Used in multiple places
// Use context-specific keys
Text("Settings.Save.Button")
Text("Document.Save.Action")
Swift Package integration creates bundle reference issues. SwiftUI Text views default to Bundle.main, but package strings extract to the package bundle. Explicitly specify the bundle:
// In Swift Packages
Text("Welcome", bundle: .module)
Button("Action", bundle: .module) { }
Runtime localization failures typically result from missing target membership or incorrect bundle parameters. Verify String Catalogs are included in the correct targets and use appropriate bundle references for frameworks and packages.
Testing localized applications effectively
Manual testing remains essential for cultural appropriateness and user experience. Change simulator languages in Settings → General → Language & Region, or configure Xcode schemes with specific languages (Edit Scheme → Options → App Language). Test with longer languages like German and shorter languages like Chinese to verify layout flexibility.
SwiftUI preview testing enables rapid iteration during development:
#Preview("English") {
ContentView()
}
#Preview("Spanish") {
ContentView()
.environment(\.locale, Locale(identifier: "es"))
}
#Preview("Arabic RTL") {
ContentView()
.environment(\.locale, Locale(identifier: "ar"))
.environment(\.layoutDirection, .rightToLeft)
}
Consider pseudolocalization for systematic layout testing. Create an artificial “language” with elongated text (150-200% of English length) to identify UI elements that don’t handle text expansion gracefully. This technique catches layout problems before sending strings to professional translators.
String Catalogs represent a fundamental shift in iOS localization, transforming a historically complex and error-prone process into an automated, maintainable system. The combination of automatic extraction, unified file management, and visual progress tracking eliminates the synchronization nightmares that have plagued iOS localization for years. By adopting these modern practices and avoiding common pitfalls, development teams can deliver truly international applications with significantly reduced effort and higher translation quality.
The technology’s backward compatibility ensures immediate adoption benefits without deployment constraints, while the JSON-based format enables sophisticated tooling integrations and version control workflows. For teams serious about international markets, String Catalogs aren’t just a new feature—they’re the foundation for sustainable, scalable localization practices that grow with your application’s global reach.
Creating a String Catalog File
- Open your Xcode project
- Right-click on your project navigator where you want to add the string catalog
- Select “New File…” or “New File from Template…” from the context menu
- Choose “String Catalog” from the Resource section
- Name your file (typically
Localizable.xcstrings
) - Select your target and click “Create”
- Build the project, it will automatically add all of your strings from the project.
Pro Tip: While the default catalog’s name is Localizeable, you should create another file with the name InfoPlist. this file will automatically have all of your app’s metadata for you to localize.
Converting Legacy .strings Files
If you have existing .strings
files, Xcode can convert them automatically.
- Select your existing
.strings
file in Project Navigator - Right-click and choose “Convert to String Catalog”
- Xcode will create a new
.xcstrings
file with all your existing translations - Review the conversion to ensure all strings transferred correctly
String catalog vs legacy's .strings file
Although there are many reasons why string catalog is worth switching to, i’ll note here the best reasons:
- Automatic string extraction from code – Xcode automatically detects and adds new localizable strings from your Swift code without manual intervention.
- String variations for different contexts and devices.
- Built-in pluralization support – Handle singular/plural forms directly in the interface without needing separate .stringsdict files.
- Dedicated UI – Visual editor within Xcode for managing translations with side-by-side language comparison and translation status indicators
Best Practices
As you scale the app, you’ll be adding more and more features and supporting more and more languages. These can add up, creating enormous string catalog files. Eventually Xcode will have trouble opening the catalog, and in some cases it will crash. Following these practices will save you time in the future and make your project more organized, maintainable, and scalable.
- Create string catalogs for chunks of content (e.g., Onboarding, Errors, Feature #1, Feature #2, etc.). Learn how to do it here.
- Add comments to strings. This rule is written in blood. We mistakenly localized the word “Share” with its financial meaning instead of its social meaning.
- Don’t over-localize – use the verbatim option or regular strings whenever localization is not necessary.