How do you localise strings: Localizable.strings, string catalogs, String(localized:)?
Predict first
What does this print when no translation exists?
print(String(localized: "Hello"))
- Hello
- nil
- An empty string
Mental model. A localisation key is a coat-check ticket. Hand it in with the language and you get the right coat back; lose the coat and you get the ticket text.
You mark user-visible text with a lookup instead of a literal: String(localized: "Hello") (iOS 15), NSLocalizedString, or in SwiftUI a string literal passed to Text, which is a LocalizedStringKey. Translations live in Localizable.strings files per language, or in a single string catalog (.xcstrings, Xcode 15) that Xcode fills in from your code and tracks per language.
If a key has no translation, the lookup returns the key itself, which is why a missing translation shows English-looking text rather than failing. LocalizedStringResource defers the lookup so a value can be passed around and resolved later in the right language. Give translators context with a comment:, use the table: and bundle: parameters for packages, and do not translate strings that are identifiers or logs.
A missing key falls back to the key text itself. That is why untranslated UI still looks fine, and why missing translations are easy to ship.
Common traps
- Building sentences by concatenation. Word order differs by language, so fragments cannot be translated. Translate whole sentences.
- Using
Text(verbatim:)where you want translation, or the reverse. Verbatim skips the lookup on purpose.
They might follow up with
- What is a good key? Either the full source-language text (natural keys) or a stable identifier like
login.title, applied consistently. - How do you test translations? Run with pseudolanguages and another Application Language in the scheme.
Related questions
- 2.6.4 How do you handle plurals and word order in localised strings?
- 2.6.5 How does the app pick its language at runtime, and how is that different from Locale?
- 2.6.2 Why does Bundle.main fail in packages, extensions and tests, and what do you use instead?
← Why does Bundle.main fail in packages, extensions and tests, and what… How do you handle plurals and word order in localised strings? →