Why Does Your App Need Internationalization?
\nImagine: you've written a cool iOS app. It works perfectly, the interface is a sight to behold, and users from Russia give it five stars. But what if your code could speak English? Or Chinese? Or Arabic, where text is read from right to left?
\nInternationalization in Swift is not just about translating buttons. It's an architectural approach that allows your app to speak the user's language, respect their cultural specifics (date formats, currencies, units of measurement), and not break when the locale changes.
\nIn this article, we'll break down how to properly implement i18n (internationalization) in a Swift project using modern Xcode tools and best practices. Ready to make your code a global citizen? Let's go!
\n\nLocalization Basics in Swift: From NSLocalizedString to String Catalog
\nThe Classic Approach: NSLocalizedString
\nThe most basic tool is the NSLocalizedString macro. It looks up translations in .strings files and, if not found, displays the key (or default value).
let hello = NSLocalizedString("hello_world", comment: "Greeting on the main screen")\nprint(hello) // Outputs the translation based on the device language\nThe comment is not just a formality. It helps translators understand the context: "hello_world" could be a greeting or a section title. Always write a meaningful comment.
\n\nString Catalog — The New Standard (Xcode 15+)
\nForget about dozens of Localizable.strings files. Xcode 15 introduced String Catalogs (.xcstrings). This is a single file with support for multiple languages, pluralization, and contextual notes.
To create a catalog: File → New → File → String Catalog. Add a key — and you're done:
// The code remains the same!\nlet welcome = NSLocalizedString("welcome_message", comment: "Greeting for a new user")\n\n// In String Catalog, you simply add translations for en, ru, ar, etc.\nPros: no need to sync files, fewer chances of encoding errors, built-in pluralization support.
\n\nFormatting Numbers and Dates: Cultural Code
\nMistake: hardcoding the date format as "dd.MM.yyyy". In the US, they write "MM/dd/yyyy", in Japan — "yyyy年MM月dd日". Swift saves the day with DateFormatter and NumberFormatter.
Dates You Can Show with Pride
\nlet date = Date()\nlet formatter = DateFormatter()\nformatter.dateStyle = .long\nformatter.timeStyle = .none\nformatter.locale = Locale.current // Automatically adapts to the device language\n\nlet localizedDate = formatter.string(from: date)\n// For en: "January 15, 2025"\n// For ru: "15 января 2025 г."\n// For de: "15. Januar 2025"\nprint(localizedDate)\nNo dateFormat strings! Use .short, .medium, .long — they already account for cultural rules.
Numbers and Currencies: Respect Commas and Dots
\nIn Russia, the number 1234.56 is written as "1 234,56", while in the US it's "1,234.56". NumberFormatter knows this:
\nlet price = 1234.56\nlet numberFormatter = NumberFormatter()\nnumberFormatter.numberStyle = .currency\nnumberFormatter.locale = Locale.current\n\nlet localizedPrice = numberFormatter.string(from: NSNumber(value: price))!\n// For en_US: "$1,234.56"\n// For ru_RU: "1 234,56 ₽"\n// For de_DE: "1.234,56 €"\nprint(localizedPrice)\n\nPluralization: One, Two, Many — Don't Guess
\nThe "one — many" rule only works in English. In Russian, there is "1 минута", "2 минуты", "5 минут". In Arabic, there are six forms! String Catalog and NSStringPluralRule solve this elegantly.
In String Catalog, you add a key, specify the type "Plural", and fill in the forms:
\n// In code