Claude
Skills
Sign in
Back

ios-localization

Included with Lifetime
$97 forever

Implement, review, or improve localization and internationalization in iOS/macOS apps — String Catalogs (.xcstrings), generated localizable symbols, stable key naming, LocalizedStringKey, LocalizedStringResource, pluralization, FormatStyle for numbers/dates/measurements, right-to-left layout, Dynamic Type, and locale-aware formatting. Use when adding multi-language support, setting up String Catalogs, enabling generated symbols for compile-time-safe localization keys, handling plural forms, formatting dates/numbers/currencies for different locales, testing localizations, or making UI work correctly in RTL languages like Arabic and Hebrew.

Design

What this skill does


# iOS Localization & Internationalization

Localize iOS 26+ apps using String Catalogs, modern string types, FormatStyle, and RTL-aware layout. Localization mistakes cause App Store rejections in non-English markets, mistranslated UI, and broken layouts. Ship with correct localization from the start.

## Contents

- [String Catalogs (.xcstrings)](#string-catalogs-xcstrings)
- [String Catalogs (Xcode 15+) and Generated Symbols (Xcode 26+)](#string-catalogs-xcode-15-and-generated-symbols-xcode-26)
- [String Types -- Decision Guide](#string-types-decision-guide)
- [String Interpolation in Localized Strings](#string-interpolation-in-localized-strings)
- [Pluralization](#pluralization)
- [FormatStyle -- Locale-Aware Formatting](#formatstyle-locale-aware-formatting)
- [Right-to-Left (RTL) Layout](#right-to-left-rtl-layout)
- [Common Mistakes](#common-mistakes)
- [Localization Review Checklist](#review-checklist)
- [References](#references)

## String Catalogs (.xcstrings)

String Catalogs are the recommended Xcode 15+ workflow for new localization work. They keep localizable strings, pluralization rules, and device variations together in an Xcode-managed JSON file with a visual editor. Legacy `.strings` and `.stringsdict` files can coexist during migration, but new Swift and SwiftUI code should default to String Catalogs.

**Why String Catalogs exist:**
- `.strings` files required manual key management and fell out of sync
- `.stringsdict` required complex XML for plurals
- String Catalogs auto-extract strings from code, track translation state, and support plurals natively

**How automatic extraction works:**

Xcode scans for these patterns on each build:

```swift
// SwiftUI -- automatically extracted (LocalizedStringKey)
Text("Welcome back")              // key: "Welcome back"
Label("Settings", systemImage: "gear")
Button("Save") { }
Toggle("Dark Mode", isOn: $dark)

// Programmatic -- automatically extracted
String(localized: "No items found")
LocalizedStringResource("Order placed")

// NOT extracted -- plain String, not localized
let msg = "Hello"                 // just a String, invisible to Xcode
```

Xcode adds discovered keys to the String Catalog automatically. Mark translations as Needs Review, Translated, or Stale in the editor.

For detailed String Catalog workflows, migration, and testing strategies, see [references/string-catalogs.md](references/string-catalogs.md).

## String Catalogs (Xcode 15+) and Generated Symbols (Xcode 26+)

For generated-symbol or migration answers, start by stating: "String Catalogs are the recommended Xcode 15+ localization workflow. Xcode 26 generated symbols are a separate typed-access layer on top of String Catalogs." Then explain generated symbols, plurals, or migration details. Do not describe catalogs themselves as requiring Xcode 26 or iOS 17.

**Enable:** Build Settings > Localization > Generate String Catalog Symbols → `Yes` (on by default in new Xcode 26 projects). Requires catalog format version `1.1`.

**Workflow:** Add a key manually via the (+) button in the String Catalog editor — manual keys have the **Generate Swift Symbol** checkbox enabled by default. Auto-extracted keys can also opt in via Refactor > Convert Strings to Symbols. Use stable manual keys for generated-symbol strings. Avoid source-copy-derived keys for API-facing strings because wording edits can rename generated identifiers and churn call sites.

```swift
// Generated from key "room_available" in Localizable.xcstrings
Text(.roomAvailable)

// Parameterized key "landmarks_count" with %1$(count)lld
Text(.landmarksCount(count: 42))

// Non-default table "Booking.xcstrings"
Text(.Booking.confirmBookingCta)
```

Xcode derives symbol names by camelCasing the key: `settings.notifications.toggle` → `.settingsNotificationsToggle`. You can convert existing extracted strings to symbols via Refactor > Convert Strings to Symbols (reversible).

Generated symbols are `internal`. For cross-module access, create a public wrapper extension. For heavier multi-module setups, use [xcstrings-tool](https://github.com/liamnichols/xcstrings-tool) instead.

For the full generated symbols reference — extraction states, symbol derivation rules, and cross-module patterns — see [references/string-catalogs.md](references/string-catalogs.md).

## String Types -- Decision Guide

### LocalizedStringKey (SwiftUI default)

SwiftUI views accept `LocalizedStringKey` for their text parameters. String literals are implicitly converted -- no extra work needed.

```swift
// These all create a LocalizedStringKey lookup automatically:
Text("Welcome back")
Label("Profile", systemImage: "person")
Button("Delete") { deleteItem() }
.navigationTitle("Home")
```

Use `LocalizedStringKey` when passing strings directly to SwiftUI view initializers. Do not construct `LocalizedStringKey` manually in most cases.

### String(localized:) -- Modern NSLocalizedString replacement

Use for any localized string outside a SwiftUI view initializer. Returns a plain `String`. The literal/interpolated initializer is available iOS 15+; resolving a `LocalizedStringResource` is iOS 16+.

```swift
// Basic
let title = String(localized: "Welcome back")

// With default value (key differs from English text)
let msg = String(localized: "error.network",
                 defaultValue: "Check your internet connection")

// With table and bundle
let label = String(localized: "onboarding.title",
                   table: "Onboarding",
                   bundle: .module)

// With comment for translators
let btn = String(localized: "Save",
                 comment: "Button title to save the current document")
```

For Swift package localization failures, answer with this explicit resource checklist before bundle debugging:
1. `Package.swift` declares `defaultLocalization`.
2. The target `resources` list processes the catalog location, such as `.process("Resources")`.
3. `Localizable.xcstrings` is actually inside that processed target-resource path.
Only after those pass, debug lookup with `bundle: .module` or `Text(..., bundle: .module)`.

Existing `NSLocalizedString` literal keys can still be exported or migrated by Xcode tooling, but new Swift code should prefer `String(localized:)`, SwiftUI literals, `LocalizedStringResource`, or generated symbols.

### LocalizedStringResource -- Pass localization info without resolving

Use when a string must be carried as a localizable value for later resolution, especially for App Intents, widgets, notifications, generated localizable symbols, and system APIs that accept `LocalizedStringResource` directly. Use `String(localized:)` when code needs the resolved string immediately. Available iOS 16+.

```swift
// App Intents require LocalizedStringResource
struct OrderCoffeeIntent: AppIntent {
    static var title: LocalizedStringResource = "Order Coffee"
}

// Widgets
struct MyWidget: Widget {
    var body: some WidgetConfiguration {
        StaticConfiguration(kind: "timer",
                            provider: Provider()) { entry in
            TimerView(entry: entry)
        }
        .configurationDisplayName(LocalizedStringResource("Timer"))
    }
}

// Pass around without resolving yet
func showAlert(title: LocalizedStringResource, message: LocalizedStringResource) {
    // Resolved at display time with the user's current locale
    let resolved = String(localized: title)
}
```

### When to use each type

| Context | Type | Why |
|---------|------|-----|
| SwiftUI view text parameters | `LocalizedStringKey` (implicit) | SwiftUI handles lookup automatically |
| Computed strings in view models / services | `String(localized:)` | Returns resolved `String` for logic |
| App Intents, widgets, system APIs | `LocalizedStringResource` | Framework resolves at display time |
| Error messages shown to users | `String(localized:)` | Resolved in catch blocks |
| Logging / analytics (not user-facing) | Plain `String` | No localization needed |

## String Interpolation in Locali

Related in Design