Best for
- Use when adding multi-language support, setting up String Catalogs, enabling generated symbols for compile-time-safe localization keys, handling plural forms, form
dpearson2699/swift-ios-skills/skills/ios-localization/SKILL.md
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, form
Decision brief
Localize Apple-platform apps with String Catalogs, modern string types, locale-aware formatting, and right-to-left layout.
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/dpearson2699/swift-ios-skills --skill "skills/ios-localization"Inspect the Agent Skill "ios-localization" from https://github.com/dpearson2699/swift-ios-skills/blob/90c9573272531337962fbb3505036d61ed23389a/skills/ios-localization/SKILL.md at commit 90c9573272531337962fbb3505036d61ed23389a. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
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:
[ ] All user-facing strings use localization (LocalizedStringKey in SwiftUI or String(localized:))
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 migr…
SwiftUI views accept LocalizedStringKey for their text parameters. String literals are implicitly converted -- no extra work needed.
SwiftUI views accept LocalizedStringKey for their text parameters. String literals are implicitly converted -- no extra work needed.
Permission review
No configured static risk pattern was detected
This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.
Evidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 91/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 933 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Localize Apple-platform apps with String Catalogs, modern string types, locale-aware formatting, and right-to-left layout.
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:
// 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")
// Plain String: not extracted or localized
let 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.
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.
// 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 instead.
For the full generated symbols reference — extraction states, symbol derivation rules, and cross-module patterns — see references/string-catalogs.md.
| Context | Type | Why |
|---|---|---|
| SwiftUI view text | LocalizedStringKey (implicit) | SwiftUI performs lookup |
| View models, services, and errors | String(localized:) | Resolves to String now |
| App Intents, widgets, deferred system UI | LocalizedStringResource | Carries localization until display |
| Non-user-facing logs and analytics | Plain String | No localization needed |
SwiftUI views accept LocalizedStringKey for their text parameters. String literals are implicitly converted -- no extra work needed.
Text("Welcome back")
Button("Delete") { deleteItem() }
Use LocalizedStringKey when passing strings directly to SwiftUI view initializers. Do not construct LocalizedStringKey manually in most cases.
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+.
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:
Package.swift declares defaultLocalization.resources list processes the catalog location, such as .process("Resources").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.
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+.
struct OrderCoffeeIntent: AppIntent {
static var title: LocalizedStringResource = "Order Coffee"
}
func showAlert(title: LocalizedStringResource, message: LocalizedStringResource) {
let resolved = String(localized: title)
}
Interpolated values in localized strings become positional arguments that translators can reorder.
// 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:
"Welcome, %@! You have %lld new messages.""%@さん、新しいメッセージが%lld件あります。"Type-safe interpolation (preferred over format specifiers):
// Interpolation provides type safety
String(localized: "Score: \(score, format: .number)")
String(localized: "Due: \(date, format: .dateTime.month().day())")
String Catalogs handle pluralization natively -- no .stringsdict XML required.
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:
| Category | English example | Arabic example |
|---|---|---|
| zero | (not used) | 0 items |
| one | 1 item | 1 item |
| two | (not used) | 2 items (dual) |
| few | (not used) | 3-10 items |
| many | (not used) | 11-99 items |
| other | 2+ items | 100+ items |
English uses only one and other. Arabic uses all six. Always supply other as the fallback.
// Code -- single interpolation triggers plural support
Text("\(unreadCount) unread messages")
// String Catalog entries (English):
// one: "%lld unread message"
// other: "%lld unread messages"
String Catalogs support device-specific text (iPhone vs iPad vs Mac):
// 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.
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.
let now = Date.now
// Preset styles
now.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-based
now.formatted(.dateTime.month(.wide).day().year())
// US: "January 15, 2026"
// In SwiftUI
Text(now, format: .dateTime.month().day().year())
let count = 1234567
count.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)
// Currency
let 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)
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 for duration, names, lists, custom styles, variant matrices, and deeper RTL testing.
SwiftUI automatically mirrors layouts for RTL languages (Arabic, Hebrew, Urdu, Persian). Most views require zero changes.
HStack children reverse order.leading / .trailing alignment and padding swap sidesNavigationStack back button moves to trailing edgeList disclosure indicators flip// Testing RTL in previews
MyView()
.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)
.leading / .trailing -- they auto-flip for RTL.left / .right -- they are fixed and break RTLHStack / VStack -- they respect layout directionoffset(x:) for directional positioning// WRONG -- German text is ~30% longer than English
Text(title).frame(width: 120)
// CORRECT
Text(title).fixedSize(horizontal: false, vertical: true)
// Or use VStack/wrapping that accommodates expansion
Testing only in English hides truncation, layout, and RTL bugs.
Use Xcode scheme settings to override the app language without changing device locale.
LocalizedStringKey in SwiftUI or String(localized:))FormatStyle, not hardcoded formats.leading / .trailing, not .left / .right.flipsForRightToLeftLayoutDirection(true)LocalizedStringResourceNSLocalizedString usage in new code@ScaledMetric used for spacing that must scale with Dynamic TypeAlternatives
github/awesome-copilot
Ensure .NET/C# code meets best practices for the solution/project.
github/awesome-copilot
Provides exact Enzyme → React Testing Library migration patterns for React 18 upgrades. Use this skill whenever Enzyme tests need to be rewritten - shallow, mount, wrapper.find(), wrapper.simulate(), wrapper.prop(), wrapper.state(), wrapper.instance(), Enzyme configure/Adapter calls, or any test file that imports from enzyme. This skill covers the full API mapping and the philosophy shift from implementation testing to behavior testing. Always read this skill before rewriting Enzyme tests - do n
coreyhaines31/marketingskills
When the user wants to plan, design, or implement an A/B test or experiment, or build a growth experimentation program. Also use when the user mentions "A/B test," "split test," "experiment," "test this change," "variant copy," "multivariate test," "hypothesis," "should I test this," "which version is better," "test two versions," "statistical significance," "how long should I run this test," "growth experiments," "experiment velocity," "experiment backlog," "ICE score," "experimentation program
event4u-app/agent-config
Use when the user says "review the design", "check the UI", or wants a comprehensive UI/UX review. Uses a 7-phase methodology covering interaction, responsiveness, accessibility, and more.