Copilot, Claude, Cursor, and others all read from ~/.agents/. The npx skills CLI handles fan-out to tool-specific directories.
5.1 KiB
5.1 KiB
| name | description | globs | ||
|---|---|---|---|---|
| Swift Localization | Localization patterns using String Catalogs and modern APIs |
|
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:
// 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:):
// 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
// BAD - Old API
let text = NSLocalizedString("Hello", comment: "Greeting")
// GOOD - Modern API
let text = String(localized: "Hello")
String Interpolation
String Catalogs handle interpolation automatically:
// 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:
// 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:
// 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:
// 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:
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:
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
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
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:
// 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
#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:
- Edit Scheme → Run → Options
- Set "Application Language" to a pseudo-language
- Look for strings that don't expand properly
Export/Import for Translation
# Export for translators
xcodebuild -exportLocalizations -project MyApp.xcodeproj -localizationPath ./Localizations
# Import translations
xcodebuild -importLocalizations -project MyApp.xcodeproj -localizationPath ./Localizations/es-MX.xcloc