What outside your code needs localising: the app name, permission prompts, shortcuts and the App Store listing?

24.8.4 · L2 · Interview

Predict first

What do the InfoPlist tables return per language?

import Foundation

let fm = FileManager.default
let root = fm.temporaryDirectory.appendingPathComponent(UUID().uuidString + ".bundle")
defer { try? fm.removeItem(at: root) }
for (lang, body) in [("en", "\"CFBundleDisplayName\" = \"Swift Bank\";\n\"NSCameraUsageDescription\" = \"Camera is used to scan cheques.\";"),
                     ("fr", "\"CFBundleDisplayName\" = \"Banque Swift\";\n\"NSCameraUsageDescription\" = \"L’appareil photo sert à scanner les chèques.\";"),
                     ("ja", "\"CFBundleDisplayName\" = \"スイフト銀行\";")] {
    let d = root.appendingPathComponent("\(lang).lproj")
    try fm.createDirectory(at: d, withIntermediateDirectories: true)
    try body.write(to: d.appendingPathComponent("InfoPlist.strings"), atomically: true, encoding: .utf8)
}
for lang in ["en", "fr", "ja"] {
    let b = Bundle(path: root.appendingPathComponent("\(lang).lproj").path)!
    print(lang, b.localizedString(forKey: "CFBundleDisplayName", value: "?", table: "InfoPlist"), "|", b.localizedString(forKey: "NSCameraUsageDescription", value: "(not translated)", table: "InfoPlist"))
}
  • en Swift Bank | Camera … / fr Banque Swift | L’appareil photo … / ja スイフト銀行 | (not translated)
  • ja スイフト銀行 | Camera is used…
  • All print Swift Bank

Mental model. Localising only the screens is translating the menu but leaving the shop sign and the door label in one language.

Not every user-visible string lives in code. The app name under the icon (CFBundleDisplayName) and the permission texts (NSCameraUsageDescription and its siblings) live in the Info.plist, and their translations go in a table called InfoPlist.strings per language (or an InfoPlist.xcstrings catalog). The check builds a bundle with English, French and Japanese tables and looks up the same keys: French gives “Banque Swift” and the translated camera sentence, Japanese gives the translated name, and the camera key that Japanese lacks comes back as the default I passed (“not translated”), which in a real app means the plain Info.plist value is used. A permission sentence in the wrong language is both a user problem and an App Review problem, so translate it; the system shows it in the language of the running app. Declare the languages you ship (CFBundleLocalizations, or the project’s Localizations list) and a development region.

The other places to do the same work: App Intents, shortcuts and widgets (their titles and parameter descriptions use their own string tables), notification text from your server (send a key plus arguments with loc-key and loc-args and let the app bundle translate, or pick the language server-side from the user’s locale, Q111), launch screens and images with text (avoid text in images), Siri and Spotlight metadata, accessibility labels and hints (Q466) and the App Store listing: name, subtitle, description, keywords and screenshots per locale in App Store Connect. Include them in the localisation export (Q474) so translators see everything in one pass, and test the permission prompts by launching in each language. What was not run: the system’s own display of these strings on a device, and the App Store Connect part.

Each language bundle returns its own table; a missing key returns the default you supplied.

Common traps

  • Translating the UI but leaving the permission strings in English. The prompt is the first thing a new user reads.
  • Sending finished English text in push notifications. Send keys and arguments or localise on the server.

They might follow up with

  • Does the app name have to change per language? No, but it can; the display name table is how.
  • How do I find strings I forgot? Run the pseudolanguage and the export for localisation.

Related questions

  • 2.6.3 How do you localise strings: Localizable.strings, string catalogs, String(localized:)?
  • 2.6.5 How does the app pick its language at runtime, and how is that different from Locale?
  • 24.4.1 How do you test internationalisation before translations exist: pseudolanguages, launch arguments, text…
  • 24.1.1 How does VoiceOver read your UI: accessibility elements, labels, values, hints and traits, and how do you fix…

← How does a string catalog (.xcstrings) work, and which runtime files…