Ios Localization

by dpearson26998d90fd121a26No license1.1K starsListed Oct 8, 2026Updated Oct 8, 2026Repository updated 2 months ago

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.

Instructions onlySoftware Development
AI-generated overview

Guides localization and internationalization of iOS/macOS apps using String Catalogs, locale-aware formatting, and RTL layout.

What it does
This skill provides instructions for implementing, reviewing, and improving localization in Apple-platform apps. It covers String Catalogs and generated symbols, choosing between LocalizedStringKey, String(localized:), and LocalizedStringResource, pluralization, FormatStyle for dates, numbers, currency, and measurements, and right-to-left layout. It also includes a review checklist and common-mistake guidance, with two reference documents for deeper detail.
When to use it
Use it when adding multi-language support to an iOS or macOS app, setting up String Catalogs, handling plural forms, formatting dates, numbers, or currencies per locale, or making UI work correctly in RTL languages such as Arabic and Hebrew.
Requirements
No scripts; instructions and reference documents only. It assumes an Apple-platform development environment with Xcode 15+ (Xcode 26 for generated symbols) and Swift/SwiftUI code.

iOS Localization & Internationalization

Localize Apple-platform apps with String Catalogs, modern string types, locale-aware formatting, and right-to-left layout.

Contents

String Catalogs and Generated Symbols

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.

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 extractedString(localized: "No items found")LocalizedStringResource("Order placed")
// Plain String: not extracted or localizedlet msg = "Hello"

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 [blocked].

Generated symbols are an Xcode 26 typed-access layer on top of String Catalogs; they do not change the catalog's Xcode 15 availability.

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.xcstringsText(.roomAvailable)
// Parameterized key "landmarks_count" with %1$(count)lldText(.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 instead.

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

String Types -- Decision Guide

ContextTypeWhy
SwiftUI view textLocalizedStringKey (implicit)SwiftUI performs lookup
View models, services, and errorsString(localized:)Resolves to String now
App Intents, widgets, deferred system UILocalizedStringResourceCarries localization until display
Non-user-facing logs and analyticsPlain StringNo localization needed

LocalizedStringKey (SwiftUI default)

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

swift
Text("Welcome back")Button("Delete") { deleteItem() }

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
let title = String(localized: "Welcome back")let msg = String(localized: "error.network",                 defaultValue: "Check your internet connection")

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
struct OrderCoffeeIntent: AppIntent {    static var title: LocalizedStringResource = "Order Coffee"}
func showAlert(title: LocalizedStringResource, message: LocalizedStringResource) {    let resolved = String(localized: title)}

String Interpolation in Localized Strings

Interpolated values in localized strings become positional arguments that translators can reorder.

swift
// English: "Welcome, Alice! You have 3 new messages."// German:  "Willkommen, Alice! Sie haben 3 neue Nachrichten."// Japanese: "Alice さん、新しいメッセージが 3 件あります。"let text = String(localized: "Welcome, \(name)! You have \(count) new messages.")

In the String Catalog, this appears with %@ and %lld placeholders that translators can reorder:

  • English: "Welcome, %@! You have %lld new messages."
  • Japanese: "%@さん、新しいメッセージが%lld件あります。"

Type-safe interpolation (preferred over format specifiers):

swift
// Interpolation provides type safetyString(localized: "Score: \(score, format: .number)")String(localized: "Due: \(date, format: .dateTime.month().day())")

Pluralization

String Catalogs handle pluralization natively -- no .stringsdict XML required.

Setup in String Catalog

When a localized string contains an integer interpolation, Xcode detects it and offers plural variants in the String Catalog editor. Supply translations for each CLDR plural category:

CategoryEnglish exampleArabic example
zero(not used)0 items
one1 item1 item
two(not used)2 items (dual)
few(not used)3-10 items
many(not used)11-99 items
other2+ items100+ items

English uses only one and other. Arabic uses all six. Always supply other as the fallback.

swift
// Code -- single interpolation triggers plural supportText("\(unreadCount) unread messages")
// String Catalog entries (English)://   one:   "%lld unread message"//   other: "%lld unread messages"

Device Variations

String Catalogs support device-specific text (iPhone vs iPad vs Mac):

swift
// In String Catalog editor, enable "Vary by Device" for a key// iPhone: "Tap to continue"// iPad:   "Tap or click to continue"// Mac:    "Click to continue"

Use Foundation's automatic grammar agreement markup when nearby words must inflect for a value's number or gender. Preserve the complete inflecting phrase for translators; see Automatic Grammar Agreement.

FormatStyle -- Locale-Aware Formatting

Never hard-code user-facing formats. Use FormatStyle and test output under contrasting locales such as en_US, de_DE, ar_SA, and ja_JP.

ios-localization owns FormatStyle guidance when the issue is locale-aware user-facing display, including numbers, dates, currency, units, names, lists, calendars, separators, and locale preview/testing. For custom FormatStyle, ParseableFormatStyle, parsing, Date.IntervalFormatStyle, URL.FormatStyle, or reusable formatter API design, route to swift-formatstyle; keep ios-localization advice to locale risks and testing unless implementation is explicitly requested.

Dates

swift
let now = Date.now
// Preset stylesnow.formatted(date: .long, time: .shortened)// US: "January 15, 2026 at 3:30 PM"// DE: "15. Januar 2026 um 15:30"// JP: "2026年1月15日 15:30"
// Component-basednow.formatted(.dateTime.month(.wide).day().year())// US: "January 15, 2026"
// In SwiftUIText(now, format: .dateTime.month().day().year())

Numbers

swift
let count = 1234567count.formatted()                     // "1,234,567" (US) / "1.234.567" (DE)count.formatted(.number.precision(.fractionLength(2)))count.formatted(.percent)             // For 0.85 -> "85%" (US) / "85 %" (FR)
// Currencylet price = Decimal(29.99)price.formatted(.currency(code: "USD"))  // "$29.99" (US) / "29,99 $US" (FR)price.formatted(.currency(code: "EUR"))  // "29,99 EUR" (DE)

Measurements

swift
let distance = Measurement(value: 5, unit: UnitLength.kilometers)distance.formatted(.measurement(width: .wide))// US: "3.1 miles" (auto-converts!) / DE: "5 Kilometer"
let temp = Measurement(value: 22, unit: UnitTemperature.celsius)temp.formatted(.measurement(width: .abbreviated))// US: "72 F" (auto-converts!) / FR: "22 C"

Load references/formatstyle-locale.md [blocked] for duration, names, lists, custom styles, variant matrices, and deeper RTL testing.

Right-to-Left (RTL) Layout

SwiftUI automatically mirrors layouts for RTL languages (Arabic, Hebrew, Urdu, Persian). Most views require zero changes.

What SwiftUI auto-mirrors

  • HStack children reverse order
  • .leading / .trailing alignment and padding swap sides
  • NavigationStack back button moves to trailing edge
  • List disclosure indicators flip
  • Text alignment follows reading direction

What needs manual attention

swift
// Testing RTL in previewsMyView()    .environment(\.layoutDirection, .rightToLeft)    .environment(\.locale, Locale(identifier: "ar"))
// Images that should mirror (directional arrows, progress indicators)Image(systemName: "chevron.right")    .flipsForRightToLeftLayoutDirection(true)
// Images that should NOT mirror: logos, photos, clocks, music notes
// Forced LTR for specific content (phone numbers, code)Text("+1 (555) 123-4567")    .environment(\.layoutDirection, .leftToRight)

Layout rules

  • DO use .leading / .trailing -- they auto-flip for RTL
  • DON'T use .left / .right -- they are fixed and break RTL
  • DO use HStack / VStack -- they respect layout direction
  • DON'T use absolute offset(x:) for directional positioning

Common Mistakes

DON'T: Use fixed-width layouts

swift
// WRONG -- German text is ~30% longer than EnglishText(title).frame(width: 120)

DO: Use flexible layouts

swift
// CORRECTText(title).fixedSize(horizontal: false, vertical: true)// Or use VStack/wrapping that accommodates expansion

DON'T: Skip pseudolocalization testing

Testing only in English hides truncation, layout, and RTL bugs.

DO: Test with German (long) and Arabic (RTL) at minimum

Use Xcode scheme settings to override the app language without changing device locale.

Review Checklist

  • All user-facing strings use localization (LocalizedStringKey in SwiftUI or String(localized:))
  • No string concatenation for user-visible text
  • Dates and numbers use FormatStyle, not hardcoded formats
  • Pluralization handled via String Catalog plural variants (not manual if/else)
  • Layout uses .leading / .trailing, not .left / .right
  • UI tested with long text (German) and RTL (Arabic)
  • String Catalog includes all target languages
  • Images needing RTL mirroring use .flipsForRightToLeftLayoutDirection(true)
  • App Intents and widgets use LocalizedStringResource
  • No NSLocalizedString usage in new code
  • Comments provided for ambiguous keys (context for translators)
  • @ScaledMetric used for spacing that must scale with Dynamic Type
  • Currency formatting uses explicit currency code, not locale default
  • Pseudolocalization tested (accented, right-to-left, double-length)
  • Manually-managed keys use stable symbol-style names, not English text as the key
  • Generate String Catalog Symbols enabled for targets with manually-managed keys
  • Ensure localized string types are Sendable; use @MainActor for locale-change UI updates

References

  • FormatStyle patterns: references/formatstyle-locale.md [blocked]
  • String Catalogs guide: references/string-catalogs.md [blocked]

Source and attribution

Source:dpearson2699/swift-ios-skillsinskills/ios-localizationat commit8d90fd1

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal

More from dpearson2699/swift-ios-skills

Widgetkit

dpearson2699

Guides implementing, reviewing, and improving WidgetKit widgets and controls for iOS, iPadOS, watchOS, and CarPlay.

Software Development1.1Kupdated 2 months ago

Weatherkit

dpearson2699

Guides iOS developers in fetching WeatherKit forecasts, alerts, and attribution using WeatherService.

Software Development1.1Kupdated 2 months ago

Vision Framework

dpearson2699

Implement computer vision features including text recognition (OCR), face detection, barcode scanning, image segmentation, object tracking, and document scanning in iOS apps. Covers both the modern Swift-native Vision API (iOS 18+) and legacy VNRequest patterns, VisionKit DataScannerViewController for live camera scanning, and CoreMLRequest/VNCoreMLRequest for custom model inference. Use when adding OCR, barcode scanning, face detection, or custom Core ML model inference with Vision.

Awaiting classification1.1Kupdated 2 months ago

Tipkit

dpearson2699

Implement and review Apple TipKit feature-discovery UI for iOS 17+ apps. Use when adding or auditing in-app tips, contextual help, coach marks, Tip, TipView, popoverTip, rules, events, actions, display frequency, testing overrides, reusable tip identifiers, or iOS 18+ TipGroup and CloudKit tip sync; avoid for generic SwiftUI navigation or layout outside tip presentation.

Awaiting classification1.1Kupdated 2 months ago

Tabletopkit

dpearson2699

Guides building multiplayer spatial board games on visionOS with Apple's TabletopKit and RealityKit.

Software Development1.1Kupdated 2 months ago

Swiftui Webkit

dpearson2699

Guides embedding and controlling web content in SwiftUI apps with WebKit for SwiftUI on iOS 26 and later.

Software Development1.1Kupdated 2 months ago