{"id":"e9b09a61-b84a-4c88-bebc-b1d17ff9cc9d","shortId":"kS3YqZ","kind":"skill","title":"ios-localization","tagline":"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","description":"# iOS Localization & Internationalization\n\nLocalize iOS 26+ apps using String Catalogs, modern string types, FormatStyle, and RTL-aware layout. Localization mistakes cause App Store rejections in non-English markets, mistranslated UI, and broken layouts. Ship with correct localization from the start.\n\n## Contents\n\n- [String Catalogs (.xcstrings)](#string-catalogs-xcstrings)\n- [Generated Localizable Symbols (Xcode 26+)](#generated-localizable-symbols-xcode-26)\n- [String Types -- Decision Guide](#string-types-decision-guide)\n- [String Interpolation in Localized Strings](#string-interpolation-in-localized-strings)\n- [Pluralization](#pluralization)\n- [FormatStyle -- Locale-Aware Formatting](#formatstyle-locale-aware-formatting)\n- [Right-to-Left (RTL) Layout](#right-to-left-rtl-layout)\n- [Common Mistakes](#common-mistakes)\n- [Localization Review Checklist](#review-checklist)\n- [References](#references)\n\n## String Catalogs (.xcstrings)\n\nString Catalogs replaced `.strings` and `.stringsdict` files starting in Xcode 15 / iOS 17. They unify all localizable strings, pluralization rules, and device variations into a single JSON-based file with a visual editor.\n\n**Why String Catalogs exist:**\n- `.strings` files required manual key management and fell out of sync\n- `.stringsdict` required complex XML for plurals\n- String Catalogs auto-extract strings from code, track translation state, and support plurals natively\n\n**How automatic extraction works:**\n\nXcode scans for these patterns on each build:\n\n```swift\n// SwiftUI -- automatically extracted (LocalizedStringKey)\nText(\"Welcome back\")              // key: \"Welcome back\"\nLabel(\"Settings\", systemImage: \"gear\")\nButton(\"Save\") { }\nToggle(\"Dark Mode\", isOn: $dark)\n\n// Programmatic -- automatically extracted\nString(localized: \"No items found\")\nLocalizedStringResource(\"Order placed\")\n\n// NOT extracted -- plain String, not localized\nlet msg = \"Hello\"                 // just a String, invisible to Xcode\n```\n\nXcode adds discovered keys to the String Catalog automatically. Mark translations as Needs Review, Translated, or Stale in the editor.\n\nFor detailed String Catalog workflows, migration, and testing strategies, see [references/string-catalogs.md](references/string-catalogs.md).\n\n## Generated Localizable Symbols (Xcode 26+)\n\nXcode 26 can generate type-safe `LocalizedStringResource` symbols from String Catalog keys, replacing stringly-typed localization with compiler-checked access.\n\n**Enable:** Build Settings > Localization > Generate String Catalog Symbols → `Yes` (on by default in new Xcode 26 projects). Requires catalog format version `1.1`.\n\n**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 symbol-style key names — not English text — so renaming UI copy never breaks code references.\n\n```swift\n// Generated from key \"room_available\" in Localizable.xcstrings\nText(.roomAvailable)\n\n// Parameterized key \"landmarks_count\" with %(count)lld\nText(.landmarksCount(count: 42))\n\n// Non-default table \"Booking.xcstrings\"\nText(.Booking.confirmBookingCta)\n```\n\nXcode 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).\n\nGenerated symbols are `internal`. For cross-module access, create a public wrapper extension. For heavier multi-module setups, use [xcstrings-tool](https://github.com/liamnichols/xcstrings-tool) instead.\n\nFor the full generated symbols reference — extraction states, symbol derivation rules, and cross-module patterns — see [references/string-catalogs.md](references/string-catalogs.md).\n\n## String Types -- Decision Guide\n\n### LocalizedStringKey (SwiftUI default)\n\nSwiftUI views accept `LocalizedStringKey` for their text parameters. String literals are implicitly converted -- no extra work needed.\n\n```swift\n// These all create a LocalizedStringKey lookup automatically:\nText(\"Welcome back\")\nLabel(\"Profile\", systemImage: \"person\")\nButton(\"Delete\") { deleteItem() }\n.navigationTitle(\"Home\")\n```\n\nUse `LocalizedStringKey` when passing strings directly to SwiftUI view initializers. Do not construct `LocalizedStringKey` manually in most cases.\n\n### String(localized:) -- Modern NSLocalizedString replacement\n\nUse for any localized string outside a SwiftUI view initializer. Returns a plain `String`. Available iOS 16+.\n\n```swift\n// Basic\nlet title = String(localized: \"Welcome back\")\n\n// With default value (key differs from English text)\nlet msg = String(localized: \"error.network\",\n                 defaultValue: \"Check your internet connection\")\n\n// With table and bundle\nlet label = String(localized: \"onboarding.title\",\n                   table: \"Onboarding\",\n                   bundle: .module)\n\n// With comment for translators\nlet btn = String(localized: \"Save\",\n                 comment: \"Button title to save the current document\")\n```\n\n### LocalizedStringResource -- Pass localization info without resolving\n\nUse when you need to pass a localized string to an API that resolves it later (App Intents, widgets, notifications, system frameworks). Available iOS 16+.\n\n```swift\n// App Intents require LocalizedStringResource\nstruct OrderCoffeeIntent: AppIntent {\n    static var title: LocalizedStringResource = \"Order Coffee\"\n}\n\n// Widgets\nstruct MyWidget: Widget {\n    var body: some WidgetConfiguration {\n        StaticConfiguration(kind: \"timer\",\n                            provider: Provider()) { entry in\n            TimerView(entry: entry)\n        }\n        .configurationDisplayName(LocalizedStringResource(\"Timer\"))\n    }\n}\n\n// Pass around without resolving yet\nfunc showAlert(title: LocalizedStringResource, message: LocalizedStringResource) {\n    // Resolved at display time with the user's current locale\n    let resolved = String(localized: title)\n}\n```\n\n### When to use each type\n\n| Context | Type | Why |\n|---------|------|-----|\n| SwiftUI view text parameters | `LocalizedStringKey` (implicit) | SwiftUI handles lookup automatically |\n| Computed strings in view models / services | `String(localized:)` | Returns resolved `String` for logic |\n| App Intents, widgets, system APIs | `LocalizedStringResource` | Framework resolves at display time |\n| Error messages shown to users | `String(localized:)` | Resolved in catch blocks |\n| Logging / analytics (not user-facing) | Plain `String` | No localization needed |\n\n## String Interpolation in Localized Strings\n\nInterpolated values in localized strings become positional arguments that translators can reorder.\n\n```swift\n// English: \"Welcome, Alice! You have 3 new messages.\"\n// German:  \"Willkommen, Alice! Sie haben 3 neue Nachrichten.\"\n// Japanese: \"Alice さん、新しいメッセージが 3 件あります。\"\nlet text = String(localized: \"Welcome, \\(name)! You have \\(count) new messages.\")\n```\n\nIn the String Catalog, this appears with `%@` and `%lld` placeholders that translators can reorder:\n- English: `\"Welcome, %@! You have %lld new messages.\"`\n- Japanese: `\"%@さん、新しいメッセージが%lld件あります。\"`\n\n**Type-safe interpolation** (preferred over format specifiers):\n```swift\n// Interpolation provides type safety\nString(localized: \"Score: \\(score, format: .number)\")\nString(localized: \"Due: \\(date, format: .dateTime.month().day())\")\n```\n\n## Pluralization\n\nString Catalogs handle pluralization natively -- no `.stringsdict` XML required.\n\n### Setup in String Catalog\n\nWhen 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:\n\n| Category | English example | Arabic example |\n|----------|----------------|----------------|\n| zero | (not used) | 0 items |\n| one | 1 item | 1 item |\n| two | (not used) | 2 items (dual) |\n| few | (not used) | 3-10 items |\n| many | (not used) | 11-99 items |\n| other | 2+ items | 100+ items |\n\nEnglish uses only `one` and `other`. Arabic uses all six. Always supply `other` as the fallback.\n\n```swift\n// Code -- single interpolation triggers plural support\nText(\"\\(unreadCount) unread messages\")\n\n// String Catalog entries (English):\n//   one:   \"%lld unread message\"\n//   other: \"%lld unread messages\"\n```\n\n### Device Variations\n\nString Catalogs support device-specific text (iPhone vs iPad vs Mac):\n\n```swift\n// In String Catalog editor, enable \"Vary by Device\" for a key\n// iPhone: \"Tap to continue\"\n// iPad:   \"Tap or click to continue\"\n// Mac:    \"Click to continue\"\n```\n\n### Grammar Agreement (iOS 17+)\n\nUse `^[...]` inflection syntax for automatic grammatical agreement:\n\n```swift\n// Automatically adjusts for gender/number in supported languages\nText(\"^[\\(count) \\(\"photo\")](inflect: true) added\")\n// English: \"1 photo added\" / \"3 photos added\"\n// Spanish: \"1 foto agregada\" / \"3 fotos agregadas\"\n```\n\n## FormatStyle -- Locale-Aware Formatting\n\nNever hard-code date, number, or measurement formats. Use `FormatStyle` (iOS 15+) so formatting adapts to the user's locale automatically.\n\n### Dates\n\n```swift\nlet now = Date.now\n\n// Preset styles\nnow.formatted(date: .long, time: .shortened)\n// US: \"January 15, 2026 at 3:30 PM\"\n// DE: \"15. Januar 2026 um 15:30\"\n// JP: \"2026年1月15日 15:30\"\n\n// Component-based\nnow.formatted(.dateTime.month(.wide).day().year())\n// US: \"January 15, 2026\"\n\n// In SwiftUI\nText(now, format: .dateTime.month().day().year())\n```\n\n### Numbers\n\n```swift\nlet count = 1234567\ncount.formatted()                     // \"1,234,567\" (US) / \"1.234.567\" (DE)\ncount.formatted(.number.precision(.fractionLength(2)))\ncount.formatted(.percent)             // For 0.85 -> \"85%\" (US) / \"85 %\" (FR)\n\n// Currency\nlet price = Decimal(29.99)\nprice.formatted(.currency(code: \"USD\"))  // \"$29.99\" (US) / \"29,99 $US\" (FR)\nprice.formatted(.currency(code: \"EUR\"))  // \"29,99 EUR\" (DE)\n```\n\n### Measurements\n\n```swift\nlet distance = Measurement(value: 5, unit: UnitLength.kilometers)\ndistance.formatted(.measurement(width: .wide))\n// US: \"3.1 miles\" (auto-converts!) / DE: \"5 Kilometer\"\n\nlet temp = Measurement(value: 22, unit: UnitTemperature.celsius)\ntemp.formatted(.measurement(width: .abbreviated))\n// US: \"72 F\" (auto-converts!) / FR: \"22 C\"\n```\n\n### Duration, PersonName, Lists\n\n```swift\n// Duration\nlet dur = Duration.seconds(3661)\ndur.formatted(.time(pattern: .hourMinuteSecond))  // \"1:01:01\"\n\n// Person names\nlet name = PersonNameComponents(givenName: \"John\", familyName: \"Doe\")\nname.formatted(.name(style: .long))   // \"John Doe\" (US) / \"Doe John\" (JP)\n\n// Lists\nlet items = [\"Apples\", \"Oranges\", \"Bananas\"]\nitems.formatted(.list(type: .and))    // \"Apples, Oranges, and Bananas\" (EN)\n                                      // \"Apples, Oranges et Bananas\" (FR)\n```\n\nFor the complete FormatStyle reference, custom styles, and RTL layout, see [references/formatstyle-locale.md](references/formatstyle-locale.md).\n\n## Right-to-Left (RTL) Layout\n\nSwiftUI automatically mirrors layouts for RTL languages (Arabic, Hebrew, Urdu, Persian). Most views require zero changes.\n\n### What SwiftUI auto-mirrors\n\n- `HStack` children reverse order\n- `.leading` / `.trailing` alignment and padding swap sides\n- `NavigationStack` back button moves to trailing edge\n- `List` disclosure indicators flip\n- Text alignment follows reading direction\n\n### What needs manual attention\n\n```swift\n// Testing RTL in previews\nMyView()\n    .environment(\\.layoutDirection, .rightToLeft)\n    .environment(\\.locale, Locale(identifier: \"ar\"))\n\n// Images that should mirror (directional arrows, progress indicators)\nImage(systemName: \"chevron.right\")\n    .flipsForRightToLeftLayoutDirection(true)\n\n// Images that should NOT mirror: logos, photos, clocks, music notes\n\n// Forced LTR for specific content (phone numbers, code)\nText(\"+1 (555) 123-4567\")\n    .environment(\\.layoutDirection, .leftToRight)\n```\n\n### Layout rules\n\n- **DO** use `.leading` / `.trailing` -- they auto-flip for RTL\n- **DON'T** use `.left` / `.right` -- they are fixed and break RTL\n- **DO** use `HStack` / `VStack` -- they respect layout direction\n- **DON'T** use absolute `offset(x:)` for directional positioning\n\n## Common Mistakes\n\n### DON'T: Use NSLocalizedString in new code\n```swift\n// WRONG -- legacy API, verbose, no compiler integration with String Catalogs\nlet title = NSLocalizedString(\"welcome_title\", comment: \"Welcome screen title\")\n```\n\n### DO: Use String(localized:) or let SwiftUI handle it\n```swift\n// CORRECT\nlet title = String(localized: \"welcome_title\",\n                   defaultValue: \"Welcome!\",\n                   comment: \"Welcome screen title\")\n// Or in SwiftUI, just:\nText(\"Welcome!\")\n```\n\n### DON'T: Concatenate localized strings\n```swift\n// WRONG -- word order varies by language\nlet greeting = String(localized: \"Hello\") + \", \" + name + \"!\"\n```\n\n### DO: Use string interpolation\n```swift\n// CORRECT -- translators can reorder placeholders\nlet greeting = String(localized: \"Hello, \\(name)!\")\n```\n\n### DON'T: Hard-code date/number formats\n```swift\n// WRONG -- US-only format\nlet formatter = DateFormatter()\nformatter.dateFormat = \"MM/dd/yyyy\"  // Meaningless in most countries\n```\n\n### DO: Use FormatStyle\n```swift\n// CORRECT -- adapts to user locale\nText(date, format: .dateTime.month().day().year())\n```\n\n### DON'T: Use fixed-width layouts\n```swift\n// WRONG -- German text is ~30% longer than English\nText(title).frame(width: 120)\n```\n\n### DO: Use flexible layouts\n```swift\n// CORRECT\nText(title).fixedSize(horizontal: false, vertical: true)\n// Or use VStack/wrapping that accommodates expansion\n```\n\n### DON'T: Use .left / .right for alignment\n```swift\n// WRONG -- does not flip for RTL\nHStack { Spacer(); text }.padding(.left, 16)\n```\n\n### DO: Use .leading / .trailing\n```swift\n// CORRECT\nHStack { Spacer(); text }.padding(.leading, 16)\n```\n\n### DON'T: Put user-facing strings as plain String outside SwiftUI\n```swift\n// WRONG -- not localized\nlet errorMessage = \"Something went wrong\"\nshowAlert(message: errorMessage)\n```\n\n### DO: Use LocalizedStringResource for deferred resolution\n```swift\n// CORRECT\nlet errorMessage = LocalizedStringResource(\"Something went wrong\")\nshowAlert(message: String(localized: errorMessage))\n```\n\n### DON'T: Use natural-language text as the key for manually-managed strings\n```swift\n// WRONG -- typo silently creates a new key, stales the old one, no compiler error\nText(\"Wlecome Back\")  // was \"Welcome Back\" -- silent localization break\n```\n\n### DO: Use stable symbol-style keys and enable generated symbols\n```swift\n// CORRECT -- key is stable; UI text lives in the catalog's default value\nText(.welcomeBack)  // generated from key \"welcome_back\" in String Catalog\n// Or without generated symbols:\nString(localized: \"welcome_back\", defaultValue: \"Welcome Back\")\n```\n\n### DON'T: Skip pseudolocalization testing\nTesting only in English hides truncation, layout, and RTL bugs.\n\n### DO: Test with German (long) and Arabic (RTL) at minimum\nUse Xcode scheme settings to override the app language without changing device locale.\n\n## Review Checklist\n\n- [ ] All user-facing strings use localization (`LocalizedStringKey` in SwiftUI or `String(localized:)`)\n- [ ] No string concatenation for user-visible text\n- [ ] Dates and numbers use `FormatStyle`, not hardcoded formats\n- [ ] Pluralization handled via String Catalog plural variants (not manual if/else)\n- [ ] Layout uses `.leading` / `.trailing`, not `.left` / `.right`\n- [ ] UI tested with long text (German) and RTL (Arabic)\n- [ ] String Catalog includes all target languages\n- [ ] Images needing RTL mirroring use `.flipsForRightToLeftLayoutDirection(true)`\n- [ ] App Intents and widgets use `LocalizedStringResource`\n- [ ] No `NSLocalizedString` usage in new code\n- [ ] Comments provided for ambiguous keys (context for translators)\n- [ ] `@ScaledMetric` used for spacing that must scale with Dynamic Type\n- [ ] Currency formatting uses explicit currency code, not locale default\n- [ ] Pseudolocalization tested (accented, right-to-left, double-length)\n- [ ] Manually-managed keys use stable symbol-style names, not English text as the key\n- [ ] Generate String Catalog Symbols enabled for targets with manually-managed keys\n- [ ] Ensure localized string types are Sendable; use @MainActor for locale-change UI updates\n\n## References\n\n- FormatStyle patterns: [references/formatstyle-locale.md](references/formatstyle-locale.md)\n- String Catalogs guide: [references/string-catalogs.md](references/string-catalogs.md)","tags":["ios","localization","swift","skills","dpearson2699","accessibility","agent-skills","ai-coding","apple","claude-code","codex-skills","cursor-skills"],"capabilities":["skill","source-dpearson2699","skill-ios-localization","topic-accessibility","topic-agent-skills","topic-ai-coding","topic-apple","topic-claude-code","topic-codex-skills","topic-cursor-skills","topic-ios","topic-ios-development","topic-liquid-glass","topic-localization","topic-mapkit"],"categories":["swift-ios-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/dpearson2699/swift-ios-skills/ios-localization","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add dpearson2699/swift-ios-skills","source_repo":"https://github.com/dpearson2699/swift-ios-skills","install_from":"skills.sh"}},"qualityScore":"0.700","qualityRationale":"deterministic score 0.70 from registry signals: · indexed on github topic:agent-skills · 599 github stars · SKILL.md body (16,534 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T18:53:42.368Z","embedding":null,"createdAt":"2026-04-18T20:33:26.811Z","updatedAt":"2026-05-18T18:53:42.368Z","lastSeenAt":"2026-05-18T18:53:42.368Z","tsv":"'+1':1455 '-10':985 '-4567':1458 '-99':991 '/liamnichols/xcstrings-tool)':500 '0':968 '0.85':1213 '01':1297,1298 '1':971,973,1103,1110,1200,1296 '1.1':364 '1.234.567':1204 '100':996 '11':990 '120':1651 '123':1457 '1234567':1198 '15':163,1133,1157,1164,1168,1172,1184 '16':604,691,1690,1702 '17':165,1080 '2':978,994,1209 '2026':1158,1166,1185 '2026年1月15日':1171 '22':1267,1281 '234':1201 '26':37,86,92,319,321,358 '29':1229,1237 '29.99':1222,1227 '3':840,848,855,984,1106,1113,1160 '3.1':1255 '30':1161,1169,1173,1643 '3661':1291 '42':441 '5':1247,1261 '555':1456 '567':1202 '72':1275 '85':1214,1216 '99':1230,1238 'abbrevi':1273 'absolut':1496 'accent':1980 'accept':530 'access':342,482 'accommod':1669 'ad':1101,1105,1108 'adapt':1136,1621 'add':284,366 'adjust':1090 'agreement':1078,1087 'agregada':1112,1115 'alic':837,845,852 'align':1384,1401,1677 'also':394 'alway':1008 'ambigu':1954 'analyt':807 'api':678,788,1514 'app':13,38,54,683,693,784,1863,1939 'appear':873 'appint':699 'appl':1321,1328,1333 'ar':1422 'arab':963,1004,1364,1852,1925 'argument':829 'around':728 'arrow':1428 'attent':1408 'auto':211,390,1258,1278,1376,1470 'auto-convert':1257,1277 'auto-extract':210,389 'auto-flip':1469 'auto-mirror':1375 'automat':224,237,258,291,552,770,1085,1089,1142,1358 'avail':426,602,689 'awar':49,118,123,1119 'back':242,245,555,612,1390,1778,1781,1816,1827,1830 'banana':1323,1331,1336 'base':181,1176 'basic':606 'becom':827 'block':805 'bodi':711 'booking.confirmbookingcta':448 'booking.xcstrings':446 'break':418,1483,1784 'broken':65 'btn':649 'bug':1845 'build':234,344 'bundl':634,642 'button':250,372,560,654,1391 'c':1282 'camelcas':454 'case':582 'catalog':15,41,76,80,151,154,189,209,290,306,331,349,361,376,871,921,932,951,1026,1040,1054,1521,1806,1819,1904,1927,2006,2036 'catch':804 'categori':959,960 'caus':53 'chang':1372,1866,2027 'check':341,627 'checkbox':385 'checklist':144,147,1870 'chevron.right':1433 'children':1379 'cldr':957 'click':1070,1074 'clock':1443 'code':215,419,1015,1124,1225,1235,1453,1510,1598,1950,1974 'coffe':705 'comment':645,653,1527,1550,1951 'common':137,140,1502 'common-mistak':139 'compil':340,1517,1774 'compiler-check':339 'complet':1340 'complex':204 'compon':1175 'component-bas':1174 'comput':771 'concaten':1562,1886 'configurationdisplaynam':724 'connect':630 'construct':577 'contain':937 'content':74,1450 'context':758,1956 'continu':1066,1072,1076 'convert':399,461,469,540,1259,1279 'copi':416 'correct':69,1541,1583,1620,1657,1696,1734,1797 'count':434,436,440,865,1097,1197 'count.formatted':1199,1206,1210 'countri':1615 'creat':483,548,1765 'cross':480,515 'cross-modul':479,514 'currenc':1218,1224,1234,1969,1973 'current':659,746 'custom':1343 'dark':253,256 'date':915,1125,1143,1151,1626,1892 'date.now':1147 'date/number':1599 'dateformatt':1609 'datetime.month':917,1178,1191,1628 'day':918,1180,1192,1629 'de':1163,1205,1240,1260 'decim':1221 'decis':95,100,523 'default':354,388,444,527,614,1808,1977 'defaultvalu':626,1548,1828 'defer':1731 'delet':561 'deleteitem':562 'deriv':450,511 'detail':304 'detect':942 'devic':174,1037,1043,1059,1867 'device-specif':1042 'differ':617 'direct':570,1404,1427,1492,1500 'disclosur':1397 'discov':285 'display':740,793 'distanc':1244 'distance.formatted':1250 'document':660 'doe':1307,1313,1315 'doubl':1986 'double-length':1985 'dual':980 'due':914 'dur':1289 'dur.formatted':1292 'durat':1283,1287 'duration.seconds':1290 'dynam':1967 'edg':1395 'editor':186,302,377,952,1055 'en':1332 'enabl':343,386,1056,1793,2008 'english':60,411,619,835,882,961,998,1028,1102,1646,1839,1999 'ensur':2016 'entri':719,722,723,1027 'environ':1415,1418,1459 'error':795,1775 'error.network':625 'errormessag':1720,1726,1736,1745 'et':1335 'eur':1236,1239 'exampl':962,964 'exist':190,462 'expans':1670 'explicit':1972 'extens':487 'extra':542 'extract':212,225,238,259,269,391,463,508 'f':1276 'face':811,1708,1874 'fallback':1013 'fals':1662 'familynam':1306 'fell':198 'file':159,182,192 'fix':1481,1635 'fixed-width':1634 'fixeds':1660 'flexibl':1654 'flip':1399,1471,1682 'flipsforrighttoleftlayoutdirect':1434,1937 'follow':1402 'forc':1446 'format':119,124,362,899,910,916,1120,1129,1135,1190,1600,1606,1627,1899,1970 'formatstyl':26,45,115,121,1116,1131,1341,1618,1896,2031 'formatstyle-locale-aware-format':120 'formatt':1608 'formatter.dateformat':1610 'foto':1111,1114 'found':264 'fr':1217,1232,1280,1337 'fractionlength':1208 'frame':1649 'framework':688,790 'full':504 'func':732 'gear':249 'gender/number':1092 'generat':17,82,88,315,323,347,382,422,474,505,1794,1812,1822,2004 'generated-localizable-symbols-xcod':87 'german':843,1640,1849,1922 'github.com':499 'github.com/liamnichols/xcstrings-tool)':498 'givennam':1304 'grammar':1077 'grammat':1086 'greet':1573,1589 'guid':96,101,524,2037 'haben':847 'handl':768,922,1538,1901 'hard':1123,1597 'hard-cod':1122,1596 'hardcod':1898 'heavier':489 'hebrew':1365 'hello':276,1576,1592 'hide':1840 'home':564 'horizont':1661 'hourminutesecond':1295 'hstack':1378,1487,1685,1697 'identifi':1421 'if/else':1909 'imag':1423,1431,1436,1932 'implement':4 'implicit':539,766 'improv':7 'includ':1928 'indic':1398,1430 'inflect':1082,1099 'info':664 'initi':574,597 'instead':501 'integ':939 'integr':1518 'intent':684,694,785,1940 'intern':477 'internation':10,34 'internet':629 'interpol':103,109,818,822,896,902,940,1017,1581 'invis':280 'io':2,32,36,164,603,690,1079,1132 'ios-loc':1 'ios/macos':12 'ipad':1048,1067 'iphon':1046,1063 'ison':255 'item':263,969,972,974,979,986,992,995,997,1320 'items.formatted':1324 'januar':1165 'januari':1156,1183 'japanes':851,889 'john':1305,1312,1316 'jp':1170,1317 'json':180 'json-bas':179 'key':21,195,243,286,332,368,379,392,408,424,432,456,616,1062,1755,1768,1791,1798,1814,1955,1991,2003,2015 'kilomet':1262 'kind':715 'label':246,556,636 'landmark':433 'landmarkscount':439 'languag':1095,1363,1571,1751,1864,1931 'later':682 'layout':50,66,130,136,1347,1356,1360,1462,1491,1637,1655,1842,1910 'layoutdirect':1416,1460 'lead':1382,1466,1693,1701,1912 'left':128,134,1354,1477,1674,1689,1915,1984 'lefttoright':1461 'legaci':1513 'length':1987 'let':274,607,621,635,648,748,857,1145,1196,1219,1243,1263,1288,1301,1319,1522,1536,1542,1572,1588,1607,1719,1735 'list':1285,1318,1325,1396 'liter':537 'live':1803 'lld':437,876,886,1030,1034 'lld件あります':892 'local':3,8,33,35,51,70,105,111,117,122,142,261,273,337,346,584,591,610,624,638,651,663,674,747,751,778,801,815,820,825,860,907,913,935,1118,1141,1419,1420,1534,1545,1563,1575,1591,1624,1718,1744,1783,1825,1868,1877,1883,1976,2017,2026 'locale-awar':116,1117 'locale-chang':2025 'localiz':18,83,89,169,316 'localizable.xcstrings':428 'localizedstringkey':23,239,525,531,550,566,578,765,1878 'localizedstringresourc':24,265,327,661,696,703,725,735,737,789,1729,1737,1944 'log':806 'logic':783 'logo':1441 'long':1152,1311,1850,1920 'longer':1644 'lookup':551,769 'ltr':1447 'mac':1050,1073 'mainactor':2023 'manag':196,1759,1990,2014 'mani':987 'manual':194,369,378,579,1407,1758,1908,1989,2013 'manually-manag':1757,1988,2012 'mark':292 'market':61 'meaningless':1612 'measur':1128,1241,1245,1251,1265,1271 'messag':736,796,842,867,888,1024,1032,1036,1725,1742 'migrat':308 'mile':1256 'minimum':1855 'mirror':1359,1377,1426,1440,1935 'mistak':52,138,141,1503 'mistransl':62 'mm/dd/yyyy':1611 'mode':254 'model':775 'modern':42,585 'modul':481,492,516,643 'move':1392 'msg':275,622 'multi':491 'multi-modul':490 'music':1444 'must':1964 'myview':1414 'mywidget':708 'nachrichten':850 'name':22,409,452,862,1300,1302,1309,1577,1593,1997 'name.formatted':1308 'nativ':222,924 'natur':1750 'natural-languag':1749 'navigationstack':1389 'navigationtitl':563 'need':295,544,670,816,1406,1933 'neue':849 'never':417,1121 'new':356,841,866,887,1509,1767,1949 'non':59,443 'non-default':442 'non-english':58 'note':1445 'notif':686 'now.formatted':1150,1177 'nslocalizedstr':586,1507,1524,1946 'number':911,1126,1194,1452,1894 'number.precision':1207 'numbers/dates/measurements':28 'offer':945 'offset':1497 'old':1771 'onboard':641 'onboarding.title':639 'one':970,1001,1029,1772 'opt':395 'orang':1322,1329,1334 'order':266,704,1381,1568 'ordercoffeeint':698 'outsid':593,1713 'overrid':1861 'pad':1386,1688,1700 'paramet':535,764 'parameter':431 'pass':568,662,672,727 'pattern':231,517,1294,2032 'percent':1211 'persian':1367 'person':559,1299 'personnam':1284 'personnamecompon':1303 'phone':1451 'photo':1098,1104,1107,1442 'place':267 'placehold':877,1587 'plain':270,600,812,1711 'plural':25,113,114,171,207,221,919,923,946,958,1019,1900,1905 'pm':1162 'posit':828,1501 'prefer':897 'preset':1148 'preview':1413 'price':1220 'price.formatted':1223,1233 'profil':557 'programmat':257 'progress':1429 'project':359 'provid':717,718,903,1952 'pseudoloc':1834,1978 'public':485 'put':1705 'read':1403 'refactor':398,468 'refer':148,149,420,507,1342,2030 'references/formatstyle-locale.md':1349,1350,2033,2034 'references/string-catalogs.md':313,314,519,520,2038,2039 'reject':56 'renam':414 'reorder':833,881,1586 'replac':155,333,587 'requir':193,203,360,695,928,1370 'resolut':1732 'resolv':666,680,730,738,749,780,791,802 'respect':1490 'return':598,779 'revers':473,1380 'review':5,143,146,296,1869 'review-checklist':145 'right':30,126,132,1352,1478,1675,1916,1982 'right-to':29 'right-to-left':125,1351,1981 'right-to-left-rtl-layout':131 'righttoleft':1417 'room':425 'roomavail':430 'rtl':48,129,135,1346,1355,1362,1411,1473,1484,1684,1844,1853,1924,1934 'rtl-awar':47 'rule':172,512,1463 'safe':326,895 'safeti':905 'save':251,652,657 'scale':1965 'scaledmetr':1959 'scan':228 'scheme':1858 'score':908,909 'screen':1529,1552 'see':312,518,1348 'sendabl':2021 'servic':776 'set':247,345,1859 'settings.notifications.toggle':457 'settingsnotificationstoggl':458 'setup':493,929 'ship':67 'shorten':1154 'showalert':733,1724,1741 'shown':797 'side':1388 'sie':846 'silent':1764,1782 'singl':178,1016 'six':1007 'skill' 'skill-ios-localization' 'skip':1833 'someth':1721,1738 'source-dpearson2699' 'space':1962 'spacer':1686,1698 'spanish':1109 'specif':1044,1449 'specifi':900 'stabl':20,404,1787,1800,1993 'stale':299,1769 'start':73,160 'state':218,509 'static':700 'staticconfigur':714 'store':55 'strategi':311 'string':14,40,43,75,79,93,98,102,106,108,112,150,153,156,170,188,191,208,213,260,271,279,289,305,330,335,348,375,400,464,470,521,536,569,583,592,601,609,623,637,650,675,750,772,777,781,800,813,817,821,826,859,870,906,912,920,931,936,950,1025,1039,1053,1520,1533,1544,1564,1574,1580,1590,1709,1712,1743,1760,1818,1824,1875,1882,1885,1903,1926,2005,2018,2035 'string-catalogs-xcstr':78 'string-interpolation-in-localized-str':107 'string-types-decision-guid':97 'stringly-typ':334 'stringsdict':158,202,926 'struct':697,707 'style':407,1149,1310,1344,1790,1996 'suppli':953,1009 'support':220,1020,1041,1094 'swap':1387 'swift':235,383,421,545,605,692,834,901,1014,1051,1088,1144,1195,1242,1286,1409,1511,1540,1565,1582,1601,1619,1638,1656,1678,1695,1715,1733,1761,1796 'swiftui':236,526,528,572,595,761,767,1187,1357,1374,1537,1556,1714,1880 'symbol':19,84,90,317,328,350,384,402,406,451,466,472,475,506,510,1789,1795,1823,1995,2007 'symbol-styl':405,1788,1994 'sync':201 'syntax':1083 'system':687,787 'systemimag':248,558 'systemnam':1432 'tabl':445,632,640 'tap':1064,1068 'target':1930,2010 'temp':1264 'temp.formatted':1270 'test':310,1410,1835,1836,1847,1918,1979 'text':240,412,429,438,447,534,553,620,763,858,1021,1045,1096,1188,1400,1454,1558,1625,1641,1647,1658,1687,1699,1752,1776,1802,1810,1891,1921,2000 'time':741,794,1153,1293 'timer':716,726 'timerview':721 'titl':608,655,702,734,752,1523,1526,1530,1543,1547,1553,1648,1659 'toggl':252 'tool':497 'topic-accessibility' 'topic-agent-skills' 'topic-ai-coding' 'topic-apple' 'topic-claude-code' 'topic-codex-skills' 'topic-cursor-skills' 'topic-ios' 'topic-ios-development' 'topic-liquid-glass' 'topic-localization' 'topic-mapkit' 'track':216 'trail':1383,1394,1467,1694,1913 'translat':217,293,297,647,831,879,954,1584,1958 'trigger':1018 'true':1100,1435,1664,1938 'truncat':1841 'two':975 'type':44,94,99,325,336,522,757,759,894,904,1326,1968,2019 'type-saf':324,893 'typo':1763 'ui':63,415,1801,1917,2028 'um':1167 'unifi':167 'unit':1248,1268 'unitlength.kilometers':1249 'unittemperature.celsius':1269 'unread':1023,1031,1035 'unreadcount':1022 'updat':2029 'urdu':1366 'us':1155,1182,1203,1215,1228,1231,1254,1274,1314,1604 'us-on':1603 'usag':1947 'usd':1226 'use':39,403,494,565,588,667,755,967,977,983,989,999,1005,1081,1130,1465,1476,1486,1495,1506,1532,1579,1617,1633,1653,1666,1673,1692,1728,1748,1786,1856,1876,1895,1911,1936,1943,1960,1971,1992,2022 'user':744,799,810,1139,1623,1707,1873,1889 'user-fac':809,1706,1872 'user-vis':1888 'valu':615,823,1246,1266,1809 'var':701,710 'vari':1057,1569 'variant':947,1906 'variat':175,1038 'verbos':1515 'version':363 'vertic':1663 'via':370,397,467,1902 'view':529,573,596,762,774,1369 'visibl':1890 'visual':185 'vs':1047,1049 'vstack':1488 'vstack/wrapping':1667 'welcom':241,244,554,611,836,861,883,1525,1528,1546,1549,1551,1559,1780,1815,1826,1829 'welcomeback':1811 'went':1722,1739 'wide':1179,1253 'widget':685,706,709,786,1942 'widgetconfigur':713 'width':1252,1272,1636,1650 'willkommen':844 'without':665,729,1821,1865 'wlecom':1777 'word':1567 'work':226,543 'workflow':307,365 'wrapper':486 'wrong':1512,1566,1602,1639,1679,1716,1723,1740,1762 'x':1498 'xcode':85,91,162,227,282,283,318,320,357,449,941,1857 'xcstring':16,77,81,152,496 'xcstrings-tool':495 'xml':205,927 'year':1181,1193,1630 'yes':351 'yet':731 'zero':965,1371 'さん':853,890 '件あります':856 '新しいメッセージが':854,891","prices":[{"id":"2c318726-3141-4192-8ae2-0fb9e04f671e","listingId":"e9b09a61-b84a-4c88-bebc-b1d17ff9cc9d","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"dpearson2699","category":"swift-ios-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T20:33:26.811Z"}],"sources":[{"listingId":"e9b09a61-b84a-4c88-bebc-b1d17ff9cc9d","source":"github","sourceId":"dpearson2699/swift-ios-skills/ios-localization","sourceUrl":"https://github.com/dpearson2699/swift-ios-skills/tree/main/skills/ios-localization","isPrimary":false,"firstSeenAt":"2026-04-18T22:01:03.068Z","lastSeenAt":"2026-05-18T18:53:42.368Z"},{"listingId":"e9b09a61-b84a-4c88-bebc-b1d17ff9cc9d","source":"skills_sh","sourceId":"dpearson2699/swift-ios-skills/ios-localization","sourceUrl":"https://skills.sh/dpearson2699/swift-ios-skills/ios-localization","isPrimary":true,"firstSeenAt":"2026-04-18T20:33:26.811Z","lastSeenAt":"2026-05-07T22:40:32.029Z"}],"details":{"listingId":"e9b09a61-b84a-4c88-bebc-b1d17ff9cc9d","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"dpearson2699","slug":"ios-localization","github":{"repo":"dpearson2699/swift-ios-skills","stars":599,"topics":["accessibility","agent-skills","ai-coding","apple","claude-code","codex-skills","cursor-skills","ios","ios-development","liquid-glass","localization","mapkit","networking","storekit","swift","swift-concurrency","swiftdata","swiftui","widgetkit","xcode"],"license":"other","html_url":"https://github.com/dpearson2699/swift-ios-skills","pushed_at":"2026-04-26T21:04:17Z","description":"Agent Skills for iOS 26+, Swift 6.3, SwiftUI, and modern Apple frameworks","skill_md_sha":"1239759e4993d409de30a2c3f8e4c8f67da781a3","skill_md_path":"skills/ios-localization/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/dpearson2699/swift-ios-skills/tree/main/skills/ios-localization"},"layout":"multi","source":"github","category":"swift-ios-skills","frontmatter":{"name":"ios-localization","description":"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."},"skills_sh_url":"https://skills.sh/dpearson2699/swift-ios-skills/ios-localization"},"updatedAt":"2026-05-18T18:53:42.368Z"}}