Convert standalone .strings files into a catalog right in your browser with a free tool, without signup or upload.
Before you migrate
Two precautions that prevent most bad surprises:
- A clean Git tree, on a dedicated branch. Everything worth checking afterwards is in the diff, so you want that diff on its own.
- A build that passes before the migration. Xcode fills the catalog by scanning your code at build time. If the project doesn't compile, the catalog stays incomplete and you won't know why.
The migration itself
In the project navigator, right-click Localizable.strings, then Migrate to String Catalog. Xcode lists the files and targets involved; the exact wording can change between Xcode versions, the idea doesn't.
The fr.lproj/Localizable.strings, de.lproj/Localizable.strings… files are replaced by a single Localizable.xcstrings holding every language. Comments from the .strings files become each key's comment.
What the file looks like
A String Catalog is JSON. A simple key:
{
"sourceLanguage" : "en",
"strings" : {
"welcome_user" : {
"comment" : "Home screen greeting",
"localizations" : {
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Bon retour, %@ !"
}
}
}
}
},
"version" : "1.0"
}
And a plural, stored under variations:
"items_left" : {
"localizations" : {
"fr" : {
"variations" : {
"plural" : {
"one" : { "stringUnit" : { "state" : "translated", "value" : "%lld article restant" } },
"other" : { "stringUnit" : { "state" : "translated", "value" : "%lld articles restants" } }
}
}
}
}
}
Note the formatting: a space before each colon, the way Xcode writes it. A tool that rewrites the file as "standard" JSON will produce a huge diff without changing a single translation.
What to review in the diff
Keys marked Stale. After a build, Xcode flags keys it can no longer find in your code. They aren't necessarily dead: a key built at runtime (NSLocalizedString(variable, comment: ""), a key read from a config file) is invisible to the scan. Check before deleting.
New keys extracted from code. The build can add strings you never put in Localizable.strings, typically SwiftUI Text("…") literals, which are localizable by default. They arrive untranslated, so the percentage Xcode shows for each language drops, which is expected.
States. Every value has a state: translated, needs_review, or new for a string nobody has translated yet. Three months from now, that's how you'll know what was actually reviewed.
Format specifiers. A SwiftUI string such as Text("\(count) items left") produces the key %lld items left. If your old .strings used %d for the same sentence, you now have two near-duplicate keys; keep the one your code really uses.
What about older iOS versions?
No need for iOS 17. The catalog is a source format: at build time, Xcode compiles it into regular .strings files inside the app bundle. Your deployment target doesn't change.
Editing the catalog outside Xcode
Xcode's editor is fine for a handful of strings. To review a whole language, translate in bulk or check placeholders, a dedicated tool helps. LocalePilot opens .xcstrings files and writes them back in Xcode's style: an unchanged catalog comes out byte-identical, an edit only changes its own lines, and an edited value moves to the translated state. Plurals and device variations are editable; keys with substitutions (%#@count@) and keys marked shouldTranslate: false are kept as they are, for you to edit in Xcode.