Best practice using NSLocalizedString
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Localization is one of those tasks that feels simple at the start but becomes painful if you get the foundation wrong. NSLocalizedString is the primary API for translating user-facing strings in iOS and macOS apps, yet many developers use it without understanding the practices that keep string files maintainable as an app grows. Getting your localization workflow right from day one saves you from costly refactors when you add your second or tenth language.
Basic Syntax and Why Comments Matter
The simplest form of NSLocalizedString takes a key and a comment:
The corresponding Localizable.strings file entry looks like this:
The comment parameter is not optional filler. It is the only context your translators will see when they encounter the string. A translator working on the Arabic version of your app has no idea where "welcome_message" appears or what tone to use without that comment. Always describe where the string appears, what it refers to, and any length constraints.
Choosing Good Keys
Avoid using the English text as the key. This approach seems convenient but causes real problems:
When English text is the key, changing the English wording means changing the key, which breaks every existing translation. Descriptive keys decouple the identifier from the content, letting you update the English text without touching other languages. They also prevent collisions when the same English word means different things in different contexts. "Cancel" on an alert and "Cancel" on a subscription page may need different translations in some languages.
Using genstrings to Extract Strings
Apple provides the genstrings command-line tool to scan your source files and generate .strings files automatically:
This produces a Localizable.strings file with all your NSLocalizedString calls. Run it periodically to catch new strings and verify that your comments are present. If a comment is an empty string, genstrings will warn you. Treat these warnings as errors.
Handling Plurals with .stringsdict
Many languages have complex plural rules. English has two forms, but Arabic has six. A plain .strings file cannot handle this. Use a .stringsdict file instead:
Use it in code the same way:
Modern Swift: String(localized:)
Starting with Swift 5.5 and iOS 15, Apple introduced a cleaner API:
This approach embeds the default English value directly in code, making it easier to read. It also works with the new String Catalog (.xcstrings) format introduced in Xcode 15, which provides a visual editor for managing translations directly in Xcode.
Organizing Strings by Feature
As your app grows, a single Localizable.strings file becomes unwieldy. Split strings into table files named by feature:
Create separate files like Settings.strings, Profile.strings, and Checkout.strings. This keeps each file small, reduces merge conflicts, and lets you hand off specific files to translators working on particular features.
Avoiding Interpolation in Keys
Never embed variables in localization keys:
Dynamic keys defeat genstrings, meaning your tooling cannot verify that all keys have translations. They also make it impossible to get compile-time warnings about missing strings.
Common Pitfalls
- Empty comment parameters: Passing
comment: ""gives translators zero context, leading to incorrect translations that you will only discover after shipping. - Using English text as keys: Any English copy change breaks all translations and creates key collision risks for words with multiple meanings.
- Concatenating localized strings: Building sentences by concatenating fragments like
NSLocalizedString("hello") + " " + namebreaks in languages with different word order. Use format strings instead. - Forgetting
.stringsdictfor plurals: Hardcoding "1 item" vs "2 items" logic fails in languages like Polish, Russian, or Arabic that have more than two plural forms. - Not testing with pseudolocalization: German text is typically 30 percent longer than English. Use Xcode's pseudolanguage or double-length pseudo-locale to catch layout issues before sending strings to translators.
Summary
- Always provide meaningful comments in
NSLocalizedStringto give translators the context they need. - Use descriptive keys like
screen_element_purposeinstead of English text to decouple identifiers from content. - Use
.stringsdictfiles for any string that involves numbers or plural forms. - Prefer
String(localized:)on iOS 15 and later for cleaner syntax and String Catalog compatibility. - Split strings into per-feature table files to keep localization files manageable.
- Run
genstringsregularly and treat missing-comment warnings as errors. - Never construct localization keys dynamically or concatenate translated fragments into sentences.

