# Info.plist localization How the app's Info.plist strings (permission-prompt descriptions, app name, copyright) are localized, and the one maintenance invariant the build's plist structure imposes. ## What gets localized macOS shows a handful of Info.plist values to the user: the `NS*UsageDescription` strings in system permission dialogs ("iTerm2 wants to use the camera."), `CFBundleName`, `CFBundleGetInfoString`, and `NSHumanReadableCopyright`. Those localizable keys are collected in `sources/InfoPlist.xcstrings`, whose **keys are the literal Info.plist key names** (not symbolic keys). Xcode recognizes the `InfoPlist.xcstrings` filename and treats it as the Info.plist localization source - it's the String Catalog successor to `InfoPlist.strings`. Brand/license values (`CFBundleName` = "iTerm2", `NSHumanReadableCopyright` = "GPL v2") are constants, so every language carries the same value as the base plist. Version keys (`CFBundleGetInfoString`, and `CFBundleShortVersionString` / `CFBundleVersion` should they ever be extracted) must have **no** non-English localizations and must be marked `shouldTranslate: false`. The base plist's version is stamped in at build time, but a localized value is a frozen literal: it compiles into `.lproj/InfoPlist.strings` and overrides the real version (a release once showed "3.7.dev" in Get Info for every non-English UI). Without a localized value the OS falls back to the base plist. Note that `shouldTranslate: false` alone does not stop existing translations from being compiled; they must be removed too. `LocalizationHygieneTests` enforces this. ## How it works (no Info.plist edit required) The localization is a purely additive overlay - no plist had to change to enable it: 1. The **base plist supplies the keys and English values.** These keys are functionally required regardless of localization (the app cannot request camera access without `NSCameraUsageDescription`), so they already existed. 2. `InfoPlist.xcstrings` holds the **extracted English source plus translations**, keyed by the same plist keys. 3. At **build** time the catalog compiles to `.lproj/InfoPlist.strings` in the app bundle (one file per language). 4. At **runtime** macOS overlays those `InfoPlist.strings` values onto the plist **by key** for the current language, falling back to the base plist value for any key not translated. The app itself does nothing; the OS does the overlay when it renders permission dialogs and Finder metadata. Adding the catalog therefore touched only `sources/InfoPlist.xcstrings`; the plist was not modified, and no `CFBundleLocalizations` / `CFBundleAllowMixedLocalizations` key was needed (the presence of the `.lproj/InfoPlist.strings` files is what the OS keys off). ## The plist-flavor invariant (important) There is **no single checked-in Info.plist.** `plists/iTerm2.plist` is a **gitignored build artifact**: the Makefile copies the flavor-specific source variant over it per build target - | target | source variant | |---|---| | `make dev` (and the default backstop) | `plists/dev-iTerm2.plist` | | `make Beta` | `plists/beta-iTerm2.plist` | | `make Nightly` | `plists/nightly-iTerm2.plist` | | `make Deployment` (release) | `plists/release-iTerm2.plist` | | preview | `plists/preview-iTerm2.plist` | The runtime overlay is **flavor-agnostic** - it keys off `NSCameraUsageDescription` etc., not off which variant was copied - so localization works for whichever flavor is built. But because `InfoPlist.xcstrings` carries a **single English source per key**, the localizable values must stay **identical across all five variants**. They are today (e.g. every variant's `NSCameraUsageDescription` is `"An application in iTerm2 wants to use the camera."`, matching the catalog). Consequences if a flavor's wording ever diverges (e.g. a beta-only permission message): - The zh/other override still applies - it's keyed by the plist key, not the English - so the diverged flavor would show the translation of the *catalog's* English, which may no longer match that flavor's intended text. - The catalog's English `source` would be stale relative to that one flavor, and a translator working from the catalog would never see the divergent wording. **Rule of thumb:** keep the localizable Info.plist values (the usage descriptions in particular) identical across `dev-`/`beta-`/`nightly-`/`release-`/`preview-iTerm2.plist`. If a flavor genuinely needs different user-facing text for one of these keys, it cannot be localized correctly through the shared catalog key - treat that as a special case rather than silently diverging the wording.