Every iOS Developer looking to take their app to the next level faces, sooner or later, a crucial challenge: globalization. In the highly competitive App Store ecosystem, limiting your application to a single language means closing the door to millions of potential users. This is where internationalization and localization come into play.
In this article, you will learn in-depth how to localize text in SwiftUI and in Swift programming in general. We will explore everything from the initial setup in Xcode to the most modern pluralization techniques, ensuring your app is ready to conquer the iOS, macOS, and watchOS markets.
1. Internationalization vs. Localization: What is the difference?
Before writing a single line in Swift, it is essential to understand the difference between these two concepts, which are often confused:
- Internationalization (i18n): This is the process of designing and preparing the architecture of your application (code and UI) so that it can adapt to different languages and regions without requiring engineering changes.
- Localization (l10n): This is the process of actively translating and adapting that internationalized content to a specific culture, region, or language (for example, translating texts from English to Spanish, or changing the date format).
Thanks to the evolution of SwiftUI and the built-in tools in Xcode, Apple has vastly simplified both processes.
2. Preparing Your Project in Xcode
To start to localize text in SwiftUI, we first need to tell Xcode which languages we are going to support.
Step 2.1: Add languages to the project
- Open your project in Xcode.
- In the left-hand navigator (Project Navigator), select the root file of your project (the blue icon).
- In the main panel, select your Project (not the Target).
- Go to the Info tab.
- Look for the Localizations section. You will see that English is usually the default development language (Base).
- Click the + button and select the language you want to add (for example, Spanish – es).
By doing this, Xcode will prepare your project to support multiple languages.
3. The String Catalogs (.xcstrings) Revolution
For years, Swift programming relied on .strings and .stringsdict files to handle translations and plurals. They were error-prone, hard to maintain, and required a lot of manual work.
Starting with Xcode 15, Apple introduced String Catalogs, a total paradigm shift that every iOS Developer should adopt.
Why use String Catalogs?
- Visual management: Everything is handled from a clean interface within Xcode.
- Automatic extraction: Xcode scans your Swift and SwiftUI code and automatically extracts the strings that need translation.
- Native support for plurals and device variations: Handling different translations whether you are on iOS, macOS, or watchOS now requires just a couple of clicks.
- State tracking: It visually tells you which strings are new (NEW), which need review (STALE), and which are ready and translated with a green checkmark.
Step 3.1: Create a String Catalog
- Press
Cmd + Nto create a new file. - Search for “String Catalog” in the search bar.
- Name it
Localizable(this is the default name Swift will look for). - Make sure it is assigned to the correct Targets (iOS, macOS, watchOS).
4. How to Localize Text in SwiftUI
SwiftUI was designed with localization in mind from day one. Many of its built-in views automatically detect if a text should be localized.
Static Texts
The Text view in SwiftUI accepts a LocalizedStringKey by default when you pass it a string literal. This means localization happens “magically.”
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
// SwiftUI will look for "Hello, World!" in your Localizable.xcstrings file
Text("Hello, World!")
.font(.largeTitle)
// It also works on buttons and other standard components
Button("Settings") {
openSettings()
}
}
}
}
The moment you build your project (or refresh the Preview), Xcode will add “Hello, World!” and “Settings” to your String Catalog. You just have to go to your Localizable.xcstrings file and fill in the Spanish column with “¡Hola, Mundo!” and “Ajustes”.
Dynamic Texts (Variables)
What happens if the text is not a literal, but comes from a variable? This is where many make mistakes.
// ❌ THIS WILL NOT BE LOCALIZED AUTOMATICALLY
let myGreeting = "Good morning"
Text(myGreeting)
When you pass a variable of type String, Text() assumes it is pure text that should not be translated (for example, a user’s name or API data). To force SwiftUI to localize a variable, you must explicitly convert it to a LocalizedStringKey:
// ✅ THIS WILL BE LOCALIZED
let myGreeting = "Good morning"
Text(LocalizedStringKey(myGreeting))
5. Localization in Swift Programming (Outside Views)
As an iOS Developer, you know that not all text lives in the user interface. Often you need to generate error messages in your ViewModels, alerts from your Managers, or local notifications using pure Swift, independently of SwiftUI.
Previously, we used the heavy NSLocalizedString macro. Today, modern Swift programming uses the String(localized:) initializer.
Example in a ViewModel:
import Foundation
@Observable
class NetworkManager {
var errorMessage: String = ""
func fetchData() {
// Network error simulation
let didFail = true
if didFail {
// Extracts the key to the String Catalog elegantly
errorMessage = String(localized: "Network connection lost. Please try again.")
}
}
}
This method is not only much cleaner than its Objective-C predecessor, but it is also fully compatible with Xcode’s String Catalogs. When compiling, that string will appear ready to be translated.
6. Mastering Plurals and Dynamic Variables
One of the biggest headaches in localization is handling variables inside text, especially when they affect grammar (pluralization).
String Interpolation in SwiftUI
You can inject variables directly into your SwiftUI texts, and the localization system will understand it perfectly.
struct MessageView: View {
let unreadCount: Int
let userName: String
var body: some View {
// The generated key will be: "Hello %@, you have %lld unread messages"
Text("Hello \(userName), you have \(unreadCount) unread messages")
}
}
Configuring Plurals in the String Catalog
In the previous example, if unreadCount is 1, the text “you have 1 unread messages” is grammatically incorrect. This is where the power of Xcode shines.
- Go to your
Localizable.xcstrings. - Find the key
Hello %@, you have %lld unread messages. - Right-click on the row corresponding to the English language.
- Select Vary by Plural.
- Xcode will automatically create the One (Singular) and Other (Plural) cases for you.
Now you can define:
- One:
Hello %@, you have %lld unread message - Other:
Hello %@, you have %lld unread messages
When translating to Spanish, you can do the same, adapting the perfect syntax (“Hola %@, tienes %lld mensaje no leído” vs “Hola %@, tienes %lld mensajes no leídos”). All this without writing extra code in SwiftUI.
7. Context and Comments for Translators
Often, the same word in English means different things depending on the context. For example, “Book” can be a noun (a thing you read) or a verb (to reserve).
As an iOS Developer, it is your responsibility to provide context to the translators (or your localization team).
Providing context in SwiftUI:
You can use the comment parameter to leave a note for translators, which will appear directly in the String Catalog.
Text("Book", comment: "This is a verb, used on the booking button")
If you need to use the same word but with different translations depending on the part of the app, you cannot use the English word as the key. Instead, use a unique identifier (a strict key) and define the default text.
Text("Action_Book_Flight", tableName: "Localizable", bundle: .main, comment: "Button to reserve a flight")
8. Cross-Platform Considerations: iOS, macOS, and watchOS
The beauty of using SwiftUI and Swift is that the code we’ve seen so far is 100% reusable across all Apple operating systems.
However, screen space varies drastically. What fits perfectly on a Mac screen in German might completely overflow an Apple Watch screen.
Device Variations in String Catalogs
String Catalogs allow you to create different translations depending on the platform.
- In your String Catalog, right-click on a language in a specific key.
- Select Vary by Device.
- Select Apple Watch.
Now you can put “Settings” for iOS/macOS, and a shorter or abbreviated term for the tiny watchOS screen, directly from Xcode and without using conditional blocks like #if os(watchOS) in your code.
9. How to Test Localization in Xcode
You don’t need to constantly change the language of your physical iPhone or Simulator to test your translations. Xcode offers multiple ways to preview localization efficiently.
Testing in SwiftUI Previews
The fastest way to see your changes while designing is to modify the Preview environment. Use the .environment modifier to force a locale.
#Preview("English") {
ContentView()
.environment(\.locale, .init(identifier: "en"))
}
#Preview("Spanish") {
ContentView()
.environment(\.locale, .init(identifier: "es"))
}
This will allow you to see your UI in both languages side by side within the Xcode Canvas.
Testing in the Simulator (Via Scheme)
If you want to test the full flow of the app:
- Go to the top bar in Xcode and click on your Scheme name (next to the simulators).
- Select Edit Scheme…
- In the Run tab, go to the Options section.
- Look for the App Language option and change it from System Language to the language you want to test (e.g., Spanish).
- Also adjust App Region if you need to test local currency or date formats.
- Run the application (
Cmd + R).
10. Best Practices and Final Tips
To consolidate yourself as an expert on this topic, keep these golden rules in mind:
- Never concatenate strings to form sentences: Avoid things like
Text("Hello " + name + ", you have " + count + " messages"). This breaks grammatical structure in languages that alter word order (like Japanese or German). Always use string interpolation\(). - Watch out for UI space: Languages like German or Russian can expand your texts by up to 50%. Use
minimumScaleFactor()or allowTextviews to occupy multiple lines usinglineLimit(nil). - Migrating Legacy Projects: If you work on Legacy code with old
.stringsfiles, Xcode 15+ allows you to automatically migrate by right-clicking the file in the Project Navigator and selecting Migrate to String Catalog.
Conclusion
Knowing how to localize text in SwiftUI and structure internationalization in your Swift projects is not just a technical “bonus”; it is a mandatory skill for any professional iOS Developer. With modern tools like Xcode’s String Catalogs, adapting your app for iOS, macOS, and watchOS to the whole world has never been so accessible, fast, and safe.
If you have any questions about this article, please contact me and I will be happy to help you . You can contact me on my X profile or on my Instagram profile.