ai-docs/assets/skills/swift-localization/SKILL.md
Matt Bruce 88e4402d38 fix: use tool-agnostic ~/.agents/ as default install path
Copilot, Claude, Cursor, and others all read from ~/.agents/.
The npx skills CLI handles fan-out to tool-specific directories.
2026-02-11 12:13:20 -06:00

229 lines
5.1 KiB
Markdown

---
name: Swift Localization
description: Localization patterns using String Catalogs and modern APIs
globs: ["**/*.swift", "**/*.xcstrings"]
---
# Localization with String Catalogs
Use **String Catalogs** (`.xcstrings` files) for localization in modern Swift projects.
## Required Language Support
At minimum, support these languages:
- **English (en)** - Base language
- **Spanish - Mexico (es-MX)**
- **French - Canada (fr-CA)**
## How String Catalogs Work
### Automatic Extraction
SwiftUI `Text` views with string literals are automatically extracted:
```swift
// Automatically added to String Catalog
Text("Hello, World!")
Text("Welcome back, \(user.name)!")
```
### Manual Extraction for Non-Text Strings
For strings outside of `Text` views, use `String(localized:)`:
```swift
// Use String(localized:) for alerts, buttons, accessibility
let title = String(localized: "Delete Item")
let message = String(localized: "Are you sure you want to delete this item?")
// With comments for translators
let greeting = String(
localized: "greeting_message",
defaultValue: "Hello!",
comment: "Greeting shown on the home screen"
)
```
## Never Use NSLocalizedString
```swift
// BAD - Old API
let text = NSLocalizedString("Hello", comment: "Greeting")
// GOOD - Modern API
let text = String(localized: "Hello")
```
## String Interpolation
String Catalogs handle interpolation automatically:
```swift
// In Swift
Text("You have \(count) items")
// In String Catalog, translators see:
// "You have %lld items"
// They can reorder: "Items: %lld" for languages that need different order
```
## Pluralization
Use automatic grammar agreement for plurals:
```swift
// Automatic pluralization
Text("^[\(count) item](inflect: true)")
// Result:
// count = 1: "1 item"
// count = 5: "5 items"
```
For complex pluralization rules, define in String Catalog with plural variants.
## Formatting Numbers, Dates, Currency
Always use formatters - they respect locale automatically:
```swift
// Numbers
Text(price, format: .currency(code: "USD"))
Text(percentage, format: .percent)
Text(count, format: .number)
// Dates
Text(date, format: .dateTime.month().day().year())
Text(date, format: .relative(presentation: .named))
// Measurements
let distance = Measurement(value: 5, unit: UnitLength.miles)
Text(distance, format: .measurement(width: .abbreviated))
```
## Localized String Keys
Use meaningful keys for complex strings:
```swift
// For simple UI text, use the text itself
Text("Settings")
Text("Cancel")
// For complex or contextual strings, use keys
Text("home.welcome.title") // Key in String Catalog
Text("profile.empty.message")
```
## Accessibility Labels
Localize all accessibility content:
```swift
Image(systemName: "heart.fill")
.accessibilityLabel(String(localized: "Favorite"))
Button { } label: {
Image(systemName: "trash")
}
.accessibilityLabel(String(localized: "Delete"))
.accessibilityHint(String(localized: "Removes this item permanently"))
```
## String Catalog Organization
### File Structure
```
App/
├── Localizable.xcstrings # Main strings
├── InfoPlist.xcstrings # Info.plist strings (app name, permissions)
└── Intents.xcstrings # Siri/Shortcuts strings (if applicable)
```
### Comments for Translators
Add comments to help translators understand context:
```swift
Text("Save", comment: "Button to save the current document")
Text("Save", comment: "Menu item to save all changes")
// These become separate entries with context
```
## Common Patterns
### Error Messages
```swift
enum AppError: LocalizedError {
case networkUnavailable
case invalidData
var errorDescription: String? {
switch self {
case .networkUnavailable:
return String(localized: "error.network.unavailable")
case .invalidData:
return String(localized: "error.data.invalid")
}
}
}
```
### Attributed Strings
```swift
var attributedGreeting: AttributedString {
var string = AttributedString(localized: "Welcome to **MyApp**!")
// Markdown formatting is preserved
return string
}
```
### Dynamic Strings from Server
For server-provided strings that need localization:
```swift
// Use a mapping approach
let serverKey = response.messageKey // e.g., "subscription_expired"
let localizedMessage = String(localized: String.LocalizationValue(serverKey))
```
## Testing Localization
### Preview with Different Locales
```swift
#Preview {
ContentView()
.environment(\.locale, Locale(identifier: "es-MX"))
}
#Preview {
ContentView()
.environment(\.locale, Locale(identifier: "fr-CA"))
}
```
### Pseudo-Localization
Enable in scheme to find truncation and layout issues:
1. Edit Scheme → Run → Options
2. Set "Application Language" to a pseudo-language
3. Look for strings that don't expand properly
## Export/Import for Translation
```bash
# Export for translators
xcodebuild -exportLocalizations -project MyApp.xcodeproj -localizationPath ./Localizations
# Import translations
xcodebuild -importLocalizations -project MyApp.xcodeproj -localizationPath ./Localizations/es-MX.xcloc
```