●MAX — Rork Max generates native Swift apps for iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and iMessage●NATIVE — It unlocks native capabilities React Native cannot reach: AR/LiDAR, Metal 3D, widgets, Dynamic Island, Live Activities, Siri Intents, and HealthKit●RN — Standard Rork builds cross-platform apps with React Native (Expo), a good fit when you want something working fast●CHOICE — Pick React Native for speed, or Rork Max when you need Apple hardware and OS integration●PRICE — Rork is free to start with paid plans from $25/mo; Rork Max is $200/mo●FLOW — Describe the app you want in plain language and Rork produces working code you can ship to the stores●MAX — Rork Max generates native Swift apps for iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and iMessage●NATIVE — It unlocks native capabilities React Native cannot reach: AR/LiDAR, Metal 3D, widgets, Dynamic Island, Live Activities, Siri Intents, and HealthKit●RN — Standard Rork builds cross-platform apps with React Native (Expo), a good fit when you want something working fast●CHOICE — Pick React Native for speed, or Rork Max when you need Apple hardware and OS integration●PRICE — Rork is free to start with paid plans from $25/mo; Rork Max is $200/mo●FLOW — Describe the app you want in plain language and Rork produces working code you can ship to the stores
Running Rork Max Swift Apps in Many Languages with String Catalog
An operational design for taking the Swift apps Rork Max generates into multiple languages, centered on the .xcstrings String Catalog. From extracting strings to handling plurals, pouring in translations, and checking for breakage, these are the lessons from keeping multilingual apps running.
Why does adding just one new language cause so much worry? Shipping several wallpaper apps across a dozen-plus languages, I felt that every time. Missed strings, plural mismatches, layouts broken by long translations. I repeated this work many times on iPhone, but once Rork Max began generating native Swift, I came to see that how you lay the foundation changes the burden dramatically.
Rork Max generates Swift apps from an English description, but in the initial state the strings are often written directly in the body. Pulling them into a String Catalog (.xcstrings) early makes later language additions surprisingly light. Here I will walk through that migration and operation in order.
Why String Catalog
The old Localizable.strings and .stringsdict leaned heavily on manual key management, and it was easy to miss when code and translations drifted apart. String Catalog has Xcode pick up strings automatically and shows untranslated, duplicate, and status (needs review / translated) entries in one list. Plurals are handled as variations within the same file, so the back-and-forth between files disappears.
What I appreciate most is that "extraction is automatic." If you use String(localized:) on the code side, new strings are pulled into the catalog on every build. Missed strings — the most common mistake in multilingual operation — essentially stop happening.
Aspect
Localizable.strings era
String Catalog (.xcstrings)
String extraction
Manual or scripted
Automatic at build time
Tracking untranslated
Visual diffing
Listed in a status column
Plurals
Separate .stringsdict file
Variations in the same file
Unused strings
Hard to notice
Marked STALE
Moving generated code onto String(localized:)
The initial code Rork Max returns is written, for example, like this.
Text("Added to favorites")Button("Try again") { retry() }
To put these on the String Catalog, you tidy the strings to go through String(localized:). SwiftUI's Text automatically makes string literals localizable, but spelling out the key and default value makes later management clear.
Namespacing keys by screen or role conveys context to translators. Keeping a form like favorite.added, where "where and what string this is" can be read, reduces mistranslation when you hand it off to a dozen-plus languages. I once left this vague and paid for it, so I always align it during the first cleanup.
Adding a String Catalog to the project is just File > New File in Xcode and choosing "String Catalog." After that, building lines up the keys referenced via String(localized:) into the .xcstrings.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦Concrete steps to migrate to .xcstrings and auto-extract strings from code
✦A design that handles plurals and regional differences safely as String Catalog variations
✦A workflow that builds translation import and breakage checks into operation, making each new language light
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
Handling plurals and regional differences as variations
The thing that always trips you up in many languages is plurals. Japanese barely distinguishes singular and plural, but English changes form between one and many, and some languages between zero, few, and many. In String Catalog you set "Vary by Plural" on the target key and write out the zero, one, other forms right there.
The code side just passes a number; the catalog handles the display branching.
For English favorite.count, you put "1 favorite" in one and "(count) favorites" in other. Even in languages with many plural tiers, like Arabic, you only add the extra forms in that language's column. Compared with editing a separate .stringsdict, the clarity is on another level.
Regional differences are absorbed the same way, through "Vary by Device" or per-language rows. Even fine adjustments like changing wording between iPad and iPhone avoid branching the code.
Building translation import and checks into operation
Adding a language comes down to a round trip: export the .xcstrings, translate, and import. You export each language's .xcloc with Xcode's Product > Export Localizations, fill in the translations, and bring them back with Import. I split work by language and always run a light check once before importing what comes back.
I look at mainly three things in the check. Whether placeholders (specifiers from %@ or \(...)) are preserved in the translation, whether any translation is left empty, and whether an extremely long translation breaks the screen. A simple script scans the .xcstrings JSON and pulls out entries whose state is not translated.
// .xcstrings is JSON. Mechanically pull out untranslated and needs-review.struct Catalog: Decodable { let strings: [String: Entry]}struct Entry: Decodable { let localizations: [String: Localization]?}struct Localization: Decodable { let stringUnit: StringUnit?}struct StringUnit: Decodable { let state: String // "translated" / "needs_review", etc. let value: String}
Read this JSON, list the keys whose state is not translated for the target language, and you can see at a glance "this language still has 12 untranslated." The more languages you add, the more this mechanical check pays off. Back when I eyed every entry by hand, a single empty field once surfaced after release and gave me a cold sweat.
Stage
What to do
Common pitfall
Extract
Update catalog via build
Dynamically composed strings not picked up
Export
Export Localizations
Exporting before adding new keys
Translate
Translate per language
Dropped placeholders
Check
Verify state mechanically
Overlooked untranslated entries
Import
Reflect via Import
Unchecked layout breakage
Catching breakage early
Layout breakage from long translations is best confirmed by switching languages on a real device, but that stops being realistic as languages grow. I also use pseudolocalization. Selecting something like "Double-Length Pseudolanguage" in the Xcode scheme launches the app in a pseudo-language with deliberately lengthened text, surfacing weak layout spots without waiting for translations.
For width-limited elements like buttons and tabs, deciding minimumScaleFactor or a wrapping policy early here reduces the per-language firefighting later. Widening the container first turned out to be the easiest order in the end.
The next step
Start with a freshly generated app, move the on-screen strings onto String(localized:) one by one, and add a single String Catalog. Even just adding English as one more language conveys the feel of automatic extraction and status display. Build up from there to plurals and mechanical checks, and you no longer have to brace yourself each time you add a language.
I am still in constant trial and error with multilingual operation, but with the foundation in place, "one more language" stops being scary. I would be glad if this helps anyone who, like me, is trying to bring an app to more regions.
Share
Thank You for Reading
Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.