String Catalogs + AI translation pipeline for 5 languages: Spanish, French, German, Japanese, Chinese (Simplified) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
5.6 KiB
5.6 KiB
App Localization Design
Overview
Localize SportsTime for international markets using Xcode String Catalogs with AI-generated translations.
Scope
What's localized:
- UI text (buttons, labels, menus, error messages) — ~200 strings
- Locale-aware formatting (dates, distances, durations, numbers)
What stays in English:
- Team names (Yankees, Lakers)
- Stadium names (Fenway Park, Madison Square Garden)
- League names (MLB, NBA, NHL, NFL)
Target Languages:
- Spanish (es)
- French (fr)
- German (de)
- Japanese (ja)
- Chinese Simplified (zh-Hans)
Translation Approach:
- AI-generated translations initially
- Iterate based on user feedback
- Easy to swap for professional translations later
Architecture
File Structure
SportsTime/
├── Resources/
│ └── Localizable.xcstrings # Single String Catalog for the app
├── Scripts/
│ └── translate.py # AI translation script
String Key Convention
Use descriptive keys rather than English text:
// Good - descriptive key
Text("trip.creation.title") // "Plan Your Trip"
Text("button.save") // "Save"
Text("error.network.offline") // "No internet connection"
// Avoid - English as key
Text("Plan Your Trip") // Fragile if English text changes
SwiftUI Integration
// Static text - automatic lookup
Text("trip.detail.header")
// Dynamic text with interpolation
Text("trip.stops.count \(count)") // Uses stringsdict for pluralization
// Programmatic access
let message = String(localized: "error.generic")
String Extraction Process
Phase 1: Audit Existing Strings
Identify hardcoded strings in:
- View files:
Text(),Label(),Button() - Alerts:
.alert("...", isPresented:) - Error messages in ViewModels
- Navigation titles:
.navigationTitle("...")
Phase 2: Replace Strings
Replace hardcoded strings with localized references:
// Before
Text("No trips planned yet")
// After
Text("home.empty.title")
Phase 3: Populate String Catalog
Add English strings to Localizable.xcstrings:
{
"sourceLanguage": "en",
"strings": {
"home.empty.title": {
"localizations": {
"en": { "stringUnit": { "value": "No trips planned yet" } }
}
}
}
}
AI Translation Pipeline
Translation Script Usage
python Scripts/translate.py --target es,fr,de,ja,zh-Hans
How It Works
- Parse
Localizable.xcstringsJSON - For each target language, find strings missing translations
- Batch strings (10-20 per API call) with context
- Write translations back to the String Catalog
- Commit the updated file to git
Prompt Strategy
You are translating a sports trip planning iOS app.
The app helps users plan road trips to attend MLB, NBA, NHL, NFL games.
Translate to Spanish (Latin America, informal "tú" form).
Keep sport names (NBA, MLB) and team names in English.
Keep translations concise for UI buttons and labels.
Strings to translate:
- home.empty.title: "No trips planned yet"
- button.create_trip: "Plan a Trip"
- trip.stops.count: "%d stops"
Pluralization
String Catalogs handle plurals natively:
"trip.stops.count": {
"localizations": {
"es": {
"variations": {
"plural": {
"one": { "stringUnit": { "value": "%d parada" } },
"other": { "stringUnit": { "value": "%d paradas" } }
}
}
}
}
}
Quality Safeguards
- Translations committed to git — reviewable in PRs
- String Catalog shows "Needs Review" state for AI-generated strings
- Easy to mark specific strings for professional review later
Locale-Aware Formatting
Dates and Times
// Relative dates ("Tomorrow", "In 3 days")
game.date.formatted(.relative(presentation: .named))
// Game date with weekday
game.date.formatted(.dateTime.weekday(.wide).month().day())
// English: "Saturday, March 15"
// German: "Samstag, 15. März"
// Japanese: "3月15日土曜日"
Distances
let distance = Measurement(value: miles, unit: UnitLength.miles)
distance.formatted(.measurement(width: .abbreviated, usage: .road))
// US: "245 mi"
// Germany: "394 km"
Driving Duration
let duration = Duration.seconds(driveTimeSeconds)
duration.formatted(.units(allowed: [.hours, .minutes], width: .abbreviated))
// English: "4 hr, 15 min"
// French: "4 h 15 min"
Numbers
let score = 1500
score.formatted(.number)
// US: "1,500"
// Germany: "1.500"
Implementation Plan
Phases
- Setup — Create
Localizable.xcstrings, add translation script toScripts/ - Extract — Audit and extract all hardcoded strings (~200)
- Translate — Run AI translation for 5 target languages
- Format Audit — Verify all dates/distances use Foundation formatters
- Test — QA each language in Simulator
- Ship — App Store metadata localization
Testing Strategy
Test in Simulator with scheme environment variable:
Product → Scheme → Edit Scheme → Run → Arguments
-AppleLanguages (es)
-AppleLocale es_MX
Key test cases per language:
- All screens render without truncation
- Plurals work correctly (0, 1, 2, many items)
- Dates/distances format correctly
- Right-to-left layout works if Arabic added later
App Store Metadata
Localize separately in App Store Connect:
- App name (can stay "SportsTime" globally)
- Subtitle, description, keywords, screenshots
Maintenance Workflow
When adding new strings:
- Add to Swift code with descriptive key
- Xcode auto-adds key to String Catalog with English value
- Run
translate.pyto generate translations - Commit updated String Catalog