Docs / iOS
iOS and macOS localization with String Catalogs, automated
Find hardcoded text in SwiftUI and UIKit, translate it into Localizable.xcstrings for every language, and wrap UIKit strings in String(localized:), in one command or pull request.
How it works
SwiftUI looks up the text of Text("…"), Button, Label, TextField, .navigationTitle and other views in the String Catalog by itself, so that code stays as written. UIKit takes plain strings, so label.text = "…", setTitle("…", for:) and alert titles become String(localized: "…").
A count with its word, "\(n) \(n == 1 ? "item" : "items")", becomes "\(n) items": the catalog varies %lld items by the count, with each language’s plural forms.
Every string goes into Localizable.xcstrings, keyed by its text the way Xcode writes it (Welcome back, %@!), with each language’s translation in the same file and numbered placeholders (%1$@, %2$lld) when there’s more than one. Text(verbatim:), image names and comments are left alone.
Before and after
The engine’s real output for this file. In a real run, the hosted model names the keys from context.
import SwiftUI
struct CartView: View {
let name: String
var body: some View {
VStack {
Text("Welcome back, \(name)!")
Button("Checkout") {}
}
.navigationTitle("Your cart")
}
}
final class LoginController: UIViewController {
let label = UILabel()
override func viewDidLoad() {
label.text = "Sign in to continue"
}
}import SwiftUI
struct CartView: View {
let name: String
var body: some View {
VStack {
Text("Welcome back, \(name)!")
Button("Checkout") {}
}
.navigationTitle("Your cart")
}
}
final class LoginController: UIViewController {
let label = UILabel()
override func viewDidLoad() {
label.text = String(localized: "Sign in to continue")
}
}{
"sourceLanguage" : "en",
"strings" : {
"Checkout" : {},
"Sign in to continue" : {},
"Welcome back, %@!" : {},
"Your cart" : {}
},
"version" : "1.0"
}What the setup adds
The first run sets up String Catalogs (Localizable.xcstrings) if the project doesn’t have it yet:
Localizable.xcstrings, found anywhere in the project or created at the root: add it to the app target in Xcode once
Run it
The CLI needs Node.js 22 or later, nothing else: npx runs it without adding a package.json to the project. No Node on your machine? The GitHub Action below runs it for you.
npx localize-dev login
# Preview, then write the changes
npx localize-dev run --dry-run -l es,fr,de
npx localize-dev run -l es,fr,de
# Or open a pull request instead
npx localize-dev pr -l es,fr,deOr add the GitHub Action and get a pull request on every push. Try it on your own code first in the playground.