@mmerterden/multi-agent-pipeline 20.7.0 → 20.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/LICENSE +0 -10
- package/docs/facts.json +1 -1
- package/manifest.json +285 -285
- package/package.json +3 -3
- package/pipeline/lib/redact.mjs +3 -2
- package/pipeline/scripts/_notices.mjs +1 -1
- package/pipeline/scripts/gen-skills-index.mjs +13 -1
- package/pipeline/scripts/pre-commit-check.sh +4 -0
- package/pipeline/skills/.skill-manifest.json +69 -69
- package/pipeline/skills/shared/README.md +70 -70
- package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
- package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
- package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
- package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
- package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
- package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
- package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
- package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
- package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
- package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
- package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +345 -277
- package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
- package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +107 -121
- package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +145 -165
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +306 -326
- package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
- package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
- package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +335 -360
- package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
- package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
- package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
- package/pipeline/skills/shared/external/authentication/SKILL.md +277 -381
- package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +135 -178
- package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
- package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
- package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
- package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
- package/pipeline/skills/shared/external/background-processing/SKILL.md +274 -384
- package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +173 -321
- package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
- package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
- package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
- package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
- package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
- package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
- package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
- package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
- package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +228 -376
- package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
- package/pipeline/skills/shared/external/core-data/SKILL.md +302 -368
- package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
- package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
- package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
- package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
- package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
- package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
- package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
- package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
- package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
- package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
- package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
- package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
- package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
- package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
- package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
- package/pipeline/skills/shared/external/device-integrity/SKILL.md +236 -353
- package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
- package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
- package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
- package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
- package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
- package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
- package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
- package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
- package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
- package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
- package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
- package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
- package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
- package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
- package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
- package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
- package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
- package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
- package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
- package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
- package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
- package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
- package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
- package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
- package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
- package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
- package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
- package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
- package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
- package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
- package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
- package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +3 -3
- package/pipeline/skills/shared/external/localization-reuse-map/example-mapping.json +1 -1
- package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +8 -7
- package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +2 -2
- package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +5 -2
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/_shared.py +100 -0
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +45 -26
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +14 -16
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +12 -5
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +2 -1
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +6 -5
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +44 -18
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +5 -2
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +10 -11
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +4 -33
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +12 -59
- package/pipeline/skills/shared/external/mapkit-location/SKILL.md +297 -267
- package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
- package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
- package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
- package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
- package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
- package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
- package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
- package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
- package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
- package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
- package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
- package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
- package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
- package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
- package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
- package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
- package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
- package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
- package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
- package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
- package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
- package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
- package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
- package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
- package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
- package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
- package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
- package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
- package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
- package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
- package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
- package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
- package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
- package/pipeline/skills/shared/external/skill-creator/template.md +7 -1
- package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
- package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
- package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
- package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
- package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
- package/pipeline/skills/shared/external/storekit/references/core-patterns.md +302 -241
- package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
- package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
- package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
- package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
- package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
- package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
- package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
- package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
- package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +304 -351
- package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
- package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
- package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
- package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
- package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
- package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
- package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
- package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
- package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
- package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
- package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
- package/pipeline/skills/shared/external/swift-security/SKILL.md +183 -162
- package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
- package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +411 -476
- package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
- package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
- package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
- package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
- package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +375 -491
- package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
- package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
- package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
- package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +397 -457
- package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
- package/pipeline/skills/shared/external/swift-testing/SKILL.md +191 -175
- package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
- package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +81 -84
- package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
- package/pipeline/skills/shared/external/swiftdata/SKILL.md +394 -256
- package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
- package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
- package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
- package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
- package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
- package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
- package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
- package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
- package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
- package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
- package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
- package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
- package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
- package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
- package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
- package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
- package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
- package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
- package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
- package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
- package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
- package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +201 -168
- package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
- package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +134 -133
- package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
- package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +111 -138
- package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
- package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
- package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
- package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
- package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
- package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
- package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
- package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
- package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
- package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
- package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
- package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
- package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
- package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
- package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
- package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
- package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
- package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
- package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
- package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
- package/pipeline/skills/shared/external/weatherkit/SKILL.md +160 -315
- package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
- package/pipeline/skills/shared/external/widgetkit/SKILL.md +224 -288
- package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +416 -719
- package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
|
@@ -1,369 +1,254 @@
|
|
|
1
|
-
# String Catalogs
|
|
1
|
+
# String Catalogs in Depth
|
|
2
2
|
|
|
3
3
|
## Contents
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
## What
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
5
|
+
1. [What a catalog is](#what-a-catalog-is)
|
|
6
|
+
2. [Creating one](#creating-one)
|
|
7
|
+
3. [What extraction picks up](#what-extraction-picks-up)
|
|
8
|
+
4. [Managing keys by hand](#managing-keys-by-hand)
|
|
9
|
+
5. [Strings outside SwiftUI](#strings-outside-swiftui)
|
|
10
|
+
6. [Bundles](#bundles)
|
|
11
|
+
7. [Swift packages and multiple modules](#swift-packages-and-multiple-modules)
|
|
12
|
+
8. [Plurals](#plurals)
|
|
13
|
+
9. [Device variants](#device-variants)
|
|
14
|
+
10. [Working with translators](#working-with-translators)
|
|
15
|
+
11. [The JSON format](#the-json-format)
|
|
16
|
+
12. [Generated symbols (Xcode 26)](#generated-symbols-xcode-26)
|
|
17
|
+
13. [Testing](#testing)
|
|
18
|
+
14. [Migrating from .strings and .stringsdict](#migrating-from-strings-and-stringsdict)
|
|
19
|
+
15. [Practices that scale](#practices-that-scale)
|
|
20
|
+
|
|
21
|
+
## What a catalog is
|
|
22
|
+
|
|
23
|
+
One `.xcstrings` file per target, stored as JSON, holds the source strings,
|
|
24
|
+
their translations, plural forms and device variants. Catalogs arrived in
|
|
25
|
+
Xcode 15 and work on every platform Xcode targets. Xcode 26 adds generated Swift
|
|
26
|
+
symbols as an extra layer; the catalog itself does not need Xcode 26.
|
|
27
|
+
|
|
28
|
+
## Creating one
|
|
29
|
+
|
|
30
|
+
1. File > New > File, choose String Catalog.
|
|
31
|
+
2. Call it `Localizable.xcstrings`. That name is the default table, the same
|
|
32
|
+
role `Localizable.strings` used to play.
|
|
33
|
+
3. Put it in the target's source folder.
|
|
34
|
+
4. Add languages under Project > Info > Localizations.
|
|
35
|
+
|
|
36
|
+
Any other name is a separate table. A file named `Help.xcstrings` is read with
|
|
37
|
+
`String(localized: "faq.title", table: "Help")`.
|
|
38
|
+
|
|
39
|
+
## What extraction picks up
|
|
40
|
+
|
|
41
|
+
The compiler finds localizable strings on every build by looking at known
|
|
42
|
+
initializers and parameter types.
|
|
35
43
|
|
|
36
44
|
```swift
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
Section("Account") { } // extracted
|
|
52
|
-
|
|
53
|
-
// NOT extracted -- computed or variable strings
|
|
54
|
-
Text(viewModel.title) // not extracted (runtime value)
|
|
55
|
-
Text(verbatim: "v1.2.3") // not extracted (verbatim skips localization)
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### Foundation (String(localized:))
|
|
59
|
-
```swift
|
|
60
|
-
String(localized: "No results found") // extracted
|
|
61
|
-
String(localized: "error.title",
|
|
62
|
-
defaultValue: "Something went wrong",
|
|
63
|
-
comment: "Generic error alert title") // extracted with default + comment
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### LocalizedStringResource
|
|
67
|
-
```swift
|
|
68
|
-
LocalizedStringResource("Order placed") // extracted
|
|
69
|
-
static var title: LocalizedStringResource = "Title" // extracted
|
|
70
|
-
```
|
|
45
|
+
// Picked up
|
|
46
|
+
Text("Welcome aboard")
|
|
47
|
+
Label("Downloads", systemImage: "arrow.down.circle")
|
|
48
|
+
Button("Retry") { }
|
|
49
|
+
Toggle("Offline mode", isOn: $offline)
|
|
50
|
+
Section("Account") { }
|
|
51
|
+
someView.navigationTitle("Library")
|
|
52
|
+
|
|
53
|
+
String(localized: "sync.failed", defaultValue: "Sync failed", comment: "Toast after a failed sync")
|
|
54
|
+
LocalizedStringResource("Open in Maps")
|
|
55
|
+
|
|
56
|
+
enum Copy {
|
|
57
|
+
static let emptyInbox: LocalizedStringResource = "Nothing new today"
|
|
58
|
+
}
|
|
71
59
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
60
|
+
// Not picked up
|
|
61
|
+
Text(viewModel.headline) // value known only at runtime
|
|
62
|
+
Text(verbatim: "v2.4.1") // opts out of localization
|
|
63
|
+
let screenName = "Library" // plain String
|
|
64
|
+
print("Library opened") // logging
|
|
77
65
|
```
|
|
78
66
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
## Manual Key Management
|
|
67
|
+
`NSLocalizedString` is legacy, but Xcode can still export its literal keys.
|
|
68
|
+
If extraction misses something, add the key in the catalog editor.
|
|
82
69
|
|
|
83
|
-
|
|
70
|
+
## Managing keys by hand
|
|
84
71
|
|
|
85
|
-
|
|
86
|
-
- **Remove key**: Select key, press Delete (marks as Stale, removed on next build if no code reference)
|
|
87
|
-
- **Edit comment**: Select key, edit the Comment field (provides translator context)
|
|
88
|
-
- **Mark state**: Right-click a translation to set Needs Review / Reviewed
|
|
89
|
-
- **Vary by plural**: Select a key, click Vary > Plural to add plural categories
|
|
90
|
-
- **Vary by device**: Select a key, click Vary > Device to add iPhone/iPad/Mac variants
|
|
72
|
+
In the editor:
|
|
91
73
|
|
|
92
|
-
|
|
74
|
+
- (+) adds a key.
|
|
75
|
+
- Delete marks a key Stale; if no code references it, the next build drops it.
|
|
76
|
+
- The Comment field gives translators context they cannot see in the UI.
|
|
77
|
+
- Right-click to mark an entry Needs Review or Reviewed.
|
|
78
|
+
- Vary > Plural and Vary > Device add variants.
|
|
93
79
|
|
|
94
|
-
|
|
80
|
+
Name keys like stable identifiers, grouped with dots:
|
|
95
81
|
|
|
96
|
-
```text
|
|
97
|
-
onboarding.welcome.title -> "Welcome"
|
|
98
|
-
onboarding.welcome.subtitle -> "Get started in minutes"
|
|
99
|
-
settings.notifications.toggle -> "Enable Notifications"
|
|
100
|
-
error.network.title -> "Connection Error"
|
|
101
|
-
error.network.message -> "Check your internet and try again"
|
|
102
82
|
```
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
let title = String(localized: "error.network.title",
|
|
108
|
-
defaultValue: "Connection Error",
|
|
109
|
-
comment: "Title for network error alert")
|
|
83
|
+
checkout.summary.title
|
|
84
|
+
checkout.summary.total
|
|
85
|
+
error.offline.title
|
|
86
|
+
error.offline.message
|
|
110
87
|
```
|
|
111
88
|
|
|
112
|
-
|
|
89
|
+
With Xcode 26 symbols these keys also become readable, predictable accessors.
|
|
90
|
+
For simple SwiftUI screens it is fine to let the literal be the key. Strings
|
|
91
|
+
that are shared, used across modules, or reworded often should get a stable key.
|
|
113
92
|
|
|
114
|
-
##
|
|
115
|
-
|
|
116
|
-
### View models, services, and utilities
|
|
93
|
+
## Strings outside SwiftUI
|
|
117
94
|
|
|
118
95
|
```swift
|
|
119
|
-
|
|
120
|
-
func
|
|
121
|
-
switch
|
|
122
|
-
case .
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
96
|
+
struct DeliveryStatusText {
|
|
97
|
+
func text(for status: DeliveryStatus, eta: Date) -> String {
|
|
98
|
+
switch status {
|
|
99
|
+
case .preparing:
|
|
100
|
+
String(localized: "delivery.preparing",
|
|
101
|
+
defaultValue: "The kitchen is preparing your order",
|
|
102
|
+
comment: "Order tracking, first step")
|
|
103
|
+
case .onTheWay:
|
|
104
|
+
String(localized: "delivery.on_the_way",
|
|
105
|
+
defaultValue: "Arriving \(eta, format: .dateTime.month().day())",
|
|
106
|
+
comment: "Order tracking with the expected day")
|
|
126
107
|
case .delivered:
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
case .processing:
|
|
131
|
-
return String(localized: "order.status.processing",
|
|
132
|
-
defaultValue: "Processing your order...",
|
|
133
|
-
comment: "Order status while being prepared")
|
|
108
|
+
String(localized: "delivery.done",
|
|
109
|
+
defaultValue: "Delivered",
|
|
110
|
+
comment: "Order tracking, final step")
|
|
134
111
|
}
|
|
135
112
|
}
|
|
136
113
|
}
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
### Specifying table and bundle
|
|
140
114
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
// From a specific bundle (framework or Swift package)
|
|
148
|
-
String(localized: "button.save",
|
|
149
|
-
table: "SharedUI",
|
|
150
|
-
bundle: .module,
|
|
151
|
-
comment: "Save button in shared component")
|
|
115
|
+
let help = String(localized: "faq.title", table: "Help")
|
|
116
|
+
let packaged = String(localized: "player.skip",
|
|
117
|
+
table: "Player",
|
|
118
|
+
bundle: .module,
|
|
119
|
+
comment: "Skips 15 seconds forward")
|
|
152
120
|
```
|
|
153
121
|
|
|
154
|
-
##
|
|
122
|
+
## Bundles
|
|
155
123
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
124
|
+
- App target: `Bundle.main` is implied, so pass nothing.
|
|
125
|
+
- Swift package: `.module` is the package's resource bundle. Use
|
|
126
|
+
`String(localized: "...", bundle: .module)`, and in SwiftUI pass it
|
|
127
|
+
explicitly, `Text("...", bundle: .module)`. `Text` does not pick up the
|
|
128
|
+
package bundle on its own.
|
|
129
|
+
- Framework: find the bundle through a class that lives in it.
|
|
161
130
|
|
|
162
|
-
### Swift Package (SPM)
|
|
163
131
|
```swift
|
|
164
|
-
|
|
165
|
-
String(localized: "Hello", bundle: .module)
|
|
132
|
+
final class ChartsBundleToken {}
|
|
166
133
|
|
|
167
|
-
|
|
168
|
-
|
|
134
|
+
let chartsBundle = Bundle(for: ChartsBundleToken.self)
|
|
135
|
+
let axisLabel = String(localized: "axis.time", bundle: chartsBundle)
|
|
169
136
|
```
|
|
170
137
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
let frameworkBundle = Bundle(for: MyFrameworkClass.self)
|
|
175
|
-
String(localized: "Hello",
|
|
176
|
-
bundle: .init(frameworkBundle.bundleURL))
|
|
177
|
-
```
|
|
138
|
+
Any code that is not in the main bundle has to name its bundle: `.module` in a
|
|
139
|
+
package, `Bundle(for:)` in a framework, or the current-target bundle macro
|
|
140
|
+
where the toolchain provides it.
|
|
178
141
|
|
|
179
|
-
##
|
|
142
|
+
## Swift packages and multiple modules
|
|
180
143
|
|
|
181
|
-
|
|
144
|
+
Every package target that shows text needs its own catalog.
|
|
182
145
|
|
|
183
|
-
### Package.swift setup
|
|
184
146
|
```swift
|
|
147
|
+
// swift-tools-version: 6.0
|
|
148
|
+
import PackageDescription
|
|
149
|
+
|
|
185
150
|
let package = Package(
|
|
186
|
-
name: "
|
|
151
|
+
name: "ProfileKit",
|
|
187
152
|
defaultLocalization: "en",
|
|
188
153
|
targets: [
|
|
189
|
-
.target(
|
|
190
|
-
name: "SharedUI",
|
|
191
|
-
dependencies: [],
|
|
192
|
-
resources: [
|
|
193
|
-
.process("Resources") // Localizable.xcstrings goes here
|
|
194
|
-
]
|
|
195
|
-
)
|
|
154
|
+
.target(name: "ProfileKit", resources: [.process("Resources")])
|
|
196
155
|
]
|
|
197
156
|
)
|
|
157
|
+
// Catalog path: Sources/ProfileKit/Resources/Localizable.xcstrings
|
|
198
158
|
```
|
|
199
159
|
|
|
200
|
-
### Directory structure
|
|
201
|
-
```text
|
|
202
|
-
Sources/
|
|
203
|
-
SharedUI/
|
|
204
|
-
Resources/
|
|
205
|
-
Localizable.xcstrings <- String Catalog for this module
|
|
206
|
-
Views/
|
|
207
|
-
ButtonStyles.swift
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
### Accessing strings from the package
|
|
211
160
|
```swift
|
|
212
|
-
|
|
213
|
-
|
|
161
|
+
public struct SignOutButton: View {
|
|
162
|
+
let action: () -> Void
|
|
214
163
|
public var body: some View {
|
|
215
|
-
Button(String(localized: "
|
|
164
|
+
Button(String(localized: "Sign out", bundle: .module), action: action)
|
|
216
165
|
}
|
|
217
166
|
}
|
|
218
167
|
```
|
|
219
168
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
For Swift package localization failures, answer with this explicit resource checklist before bundle debugging:
|
|
223
|
-
1. `Package.swift` declares `defaultLocalization`.
|
|
224
|
-
2. The target `resources` list processes the catalog location, such as `.process("Resources")`.
|
|
225
|
-
3. `Localizable.xcstrings` is actually inside that processed target-resource path.
|
|
226
|
-
Only after those pass, debug lookup with `bundle: .module` or `Text(..., bundle: .module)`.
|
|
227
|
-
|
|
228
|
-
## Pluralization in String Catalogs
|
|
229
|
-
|
|
230
|
-
### Setup
|
|
169
|
+
When a package keeps falling back to the development language, check setup
|
|
170
|
+
before lookup:
|
|
231
171
|
|
|
232
|
-
1.
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
2. Build the project -- Xcode adds the key to the String Catalog
|
|
237
|
-
3. Open the String Catalog, select the key
|
|
238
|
-
4. Click "Vary by Plural" in the inspector
|
|
239
|
-
5. Fill in plural forms for each language
|
|
172
|
+
1. `defaultLocalization` is set in `Package.swift`.
|
|
173
|
+
2. The target's `resources` processes the catalog's folder, for example
|
|
174
|
+
`.process("Resources")`.
|
|
175
|
+
3. The catalog file is actually inside that folder.
|
|
240
176
|
|
|
241
|
-
|
|
242
|
-
```text
|
|
243
|
-
one: "%1$(itemCount)lld item in your cart"
|
|
244
|
-
other: "%1$(itemCount)lld items in your cart"
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
### Arabic plural forms (all six categories)
|
|
248
|
-
```text
|
|
249
|
-
zero: "لا توجد عناصر في سلتك"
|
|
250
|
-
one: "عنصر واحد في سلتك"
|
|
251
|
-
two: "عنصران في سلتك"
|
|
252
|
-
few: "%lld عناصر في سلتك" (3-10)
|
|
253
|
-
many: "%lld عنصرًا في سلتك" (11-99)
|
|
254
|
-
other: "%lld عنصر في سلتك" (100+)
|
|
255
|
-
```
|
|
177
|
+
## Plurals
|
|
256
178
|
|
|
257
|
-
|
|
179
|
+
1. Write the string with an integer interpolation.
|
|
180
|
+
2. Build.
|
|
181
|
+
3. Select the key in the catalog.
|
|
182
|
+
4. Choose Vary by Plural in the inspector.
|
|
183
|
+
5. Fill each form every language needs.
|
|
258
184
|
|
|
259
|
-
|
|
185
|
+
English, with a named positional placeholder:
|
|
260
186
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
187
|
+
| Category | Value |
|
|
188
|
+
|---|---|
|
|
189
|
+
| one | `%1$(songCount)lld song` |
|
|
190
|
+
| other | `%1$(songCount)lld songs` |
|
|
265
191
|
|
|
266
|
-
|
|
192
|
+
Arabic fills all six categories: zero, one, two, few (`%lld` ...), many
|
|
193
|
+
(`%lld` ...) and other (`%lld` ...), each with its own wording.
|
|
267
194
|
|
|
268
|
-
|
|
195
|
+
Two integer interpolations in one string create a matrix. For
|
|
196
|
+
`"\(photos) photos in \(albums) albums"` English needs one/one, one/other,
|
|
197
|
+
other/one and other/other.
|
|
269
198
|
|
|
270
|
-
|
|
271
|
-
// Code is the same everywhere:
|
|
272
|
-
Text("Tap to continue")
|
|
273
|
-
|
|
274
|
-
// String Catalog provides:
|
|
275
|
-
// iPhone: "Tap to continue"
|
|
276
|
-
// iPad: "Tap or click to continue"
|
|
277
|
-
// Mac: "Click to continue"
|
|
278
|
-
// Vision: "Look and tap to continue"
|
|
279
|
-
```
|
|
199
|
+
## Device variants
|
|
280
200
|
|
|
281
|
-
|
|
201
|
+
Vary by Device lets a key read differently on iPhone, iPad, Apple Watch, Mac,
|
|
202
|
+
Apple TV and Apple Vision Pro. The Swift call site does not change.
|
|
282
203
|
|
|
283
|
-
|
|
204
|
+
## Working with translators
|
|
284
205
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
3. Xcode creates `.xcloc` bundles (one per language)
|
|
288
|
-
4. Send `.xcloc` files to translators (they contain XLIFF 1.2 inside)
|
|
206
|
+
Export with Product > Export Localizations... or on the command line. The
|
|
207
|
+
result is one `.xcloc` bundle per language, with XLIFF 1.2 inside.
|
|
289
208
|
|
|
290
|
-
### Command-line export
|
|
291
209
|
```bash
|
|
292
210
|
xcodebuild -exportLocalizations \
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
211
|
+
-project Journal.xcodeproj \
|
|
212
|
+
-localizationPath ./l10n-out \
|
|
213
|
+
-exportLanguage fr \
|
|
214
|
+
-exportLanguage ko
|
|
296
215
|
```
|
|
297
216
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
1. Product > Import Localizations...
|
|
301
|
-
2. Select the completed `.xcloc` file
|
|
302
|
-
3. Xcode merges translations into the String Catalog
|
|
303
|
-
4. Review changes in the diff viewer
|
|
217
|
+
Bring translations back with Product > Import Localizations...; Xcode merges
|
|
218
|
+
them into the catalog, and the diff viewer shows what changed.
|
|
304
219
|
|
|
305
|
-
### Command-line import
|
|
306
220
|
```bash
|
|
307
221
|
xcodebuild -importLocalizations \
|
|
308
|
-
|
|
309
|
-
|
|
222
|
+
-project Journal.xcodeproj \
|
|
223
|
+
-localizationPath ./l10n-in/fr.xcloc
|
|
310
224
|
```
|
|
311
225
|
|
|
312
|
-
##
|
|
226
|
+
## The JSON format
|
|
313
227
|
|
|
314
|
-
|
|
228
|
+
Edit through the Xcode editor or the export/import round trip where possible.
|
|
229
|
+
Knowing the format helps when a parser-based check or a careful bulk edit is
|
|
230
|
+
needed; validate any scripted change before committing it.
|
|
315
231
|
|
|
316
232
|
```json
|
|
317
233
|
{
|
|
318
|
-
"sourceLanguage": "en",
|
|
319
|
-
"version": "1.
|
|
320
|
-
"strings": {
|
|
321
|
-
"
|
|
322
|
-
"comment": "
|
|
323
|
-
"
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
"value": "Welcome, %@!"
|
|
328
|
-
}
|
|
329
|
-
},
|
|
330
|
-
"de": {
|
|
331
|
-
"stringUnit": {
|
|
332
|
-
"state": "translated",
|
|
333
|
-
"value": "Willkommen, %@!"
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
},
|
|
338
|
-
"item_available": {
|
|
339
|
-
"comment": "Button label on product search results",
|
|
340
|
-
"extractionState": "manual",
|
|
341
|
-
"localizations": {
|
|
342
|
-
"en": {
|
|
343
|
-
"stringUnit": {
|
|
344
|
-
"state": "translated",
|
|
345
|
-
"value": "Add this item"
|
|
346
|
-
}
|
|
347
|
-
}
|
|
234
|
+
"sourceLanguage" : "en",
|
|
235
|
+
"version" : "1.1",
|
|
236
|
+
"strings" : {
|
|
237
|
+
"cart_empty" : {
|
|
238
|
+
"comment" : "Shown when the cart has no items",
|
|
239
|
+
"extractionState" : "manual",
|
|
240
|
+
"localizations" : {
|
|
241
|
+
"en" : { "stringUnit" : { "state" : "translated", "value" : "Your cart is empty" } },
|
|
242
|
+
"de" : { "stringUnit" : { "state" : "translated", "value" : "Dein Warenkorb ist leer" } }
|
|
348
243
|
}
|
|
349
244
|
},
|
|
350
|
-
"
|
|
351
|
-
"localizations": {
|
|
352
|
-
"en": {
|
|
353
|
-
"variations": {
|
|
354
|
-
"plural": {
|
|
355
|
-
"one": {
|
|
356
|
-
|
|
357
|
-
"state": "translated",
|
|
358
|
-
"value": "%1$(count)lld item"
|
|
359
|
-
}
|
|
360
|
-
},
|
|
361
|
-
"other": {
|
|
362
|
-
"stringUnit": {
|
|
363
|
-
"state": "translated",
|
|
364
|
-
"value": "%1$(count)lld items"
|
|
365
|
-
}
|
|
366
|
-
}
|
|
245
|
+
"reviews_count" : {
|
|
246
|
+
"localizations" : {
|
|
247
|
+
"en" : {
|
|
248
|
+
"variations" : {
|
|
249
|
+
"plural" : {
|
|
250
|
+
"one" : { "stringUnit" : { "state" : "translated", "value" : "%lld review" } },
|
|
251
|
+
"other" : { "stringUnit" : { "state" : "translated", "value" : "%lld reviews" } }
|
|
367
252
|
}
|
|
368
253
|
}
|
|
369
254
|
}
|
|
@@ -373,199 +258,187 @@ The `.xcstrings` file is Xcode-managed JSON. Understanding the observed structur
|
|
|
373
258
|
}
|
|
374
259
|
```
|
|
375
260
|
|
|
376
|
-
|
|
261
|
+
The top level has `sourceLanguage`, `version` and `strings`. Each key may hold
|
|
262
|
+
`comment`, `extractionState` and `localizations`, where every language has a
|
|
263
|
+
`stringUnit` with `state` and `value`. Plurals nest under
|
|
264
|
+
`localizations.<lang>.variations.plural.<category>.stringUnit`. Note that
|
|
265
|
+
`cart_empty` is a manual key: the English copy lives in `value`, not in the key.
|
|
377
266
|
|
|
378
|
-
|
|
379
|
-
- `"new"` -- Xcode extracted the key but no translation exists
|
|
380
|
-
- `"translated"` -- Translation provided
|
|
381
|
-
- `"needs_review"` -- Marked for review (source string changed or manual flag)
|
|
382
|
-
- `"stale"` -- Key no longer found in code (removed on next clean build)
|
|
267
|
+
Translation `state`:
|
|
383
268
|
|
|
384
|
-
|
|
269
|
+
| State | Meaning |
|
|
270
|
+
|---|---|
|
|
271
|
+
| `new` | Not translated yet |
|
|
272
|
+
| `translated` | Done |
|
|
273
|
+
| `needs_review` | Source text changed or someone flagged it |
|
|
274
|
+
| `stale` | The key is gone from code; a clean build removes it |
|
|
385
275
|
|
|
386
|
-
|
|
276
|
+
`extractionState`:
|
|
387
277
|
|
|
388
|
-
|
|
|
389
|
-
|
|
390
|
-
| `extracted_with_value` |
|
|
391
|
-
| `manual` | Added by hand
|
|
392
|
-
| `stale` |
|
|
393
|
-
| `migrated` |
|
|
278
|
+
| State | Meaning |
|
|
279
|
+
|---|---|
|
|
280
|
+
| `extracted_with_value` | Found in source by the build |
|
|
281
|
+
| `manual` | Added by hand; build sync never changes or deletes it |
|
|
282
|
+
| `stale` | No longer found; its translations are orphaned |
|
|
283
|
+
| `migrated` | Came from `.strings` or `.stringsdict` |
|
|
394
284
|
|
|
395
|
-
|
|
285
|
+
Manual keys have Generate Swift Symbol on by default and, with the build
|
|
286
|
+
setting on, turn into compiler-checked `LocalizedStringResource` accessors.
|
|
287
|
+
Extracted keys opt in one at a time or through Convert Strings to Symbols.
|
|
396
288
|
|
|
397
|
-
## Generated
|
|
289
|
+
## Generated symbols (Xcode 26)
|
|
398
290
|
|
|
399
|
-
|
|
291
|
+
Frame answers the same way as in the main skill: catalogs are the Xcode 15+
|
|
292
|
+
workflow; generated symbols are Xcode 26's typed layer on top. Never state that
|
|
293
|
+
catalogs need Xcode 26 or iOS 17.
|
|
400
294
|
|
|
401
|
-
|
|
295
|
+
Xcode 26 writes format version `"1.1"` into the file as soon as symbol metadata
|
|
296
|
+
exists.
|
|
402
297
|
|
|
403
|
-
|
|
404
|
-
2. The catalog must use format version `"1.1"` - Xcode 26 writes this automatically when symbol generation metadata is present
|
|
405
|
-
3. Each key has a **Generate Swift Symbol** checkbox in the String Catalog editor. Manual keys (added via the (+) button) have this enabled by default. Auto-extracted keys can opt in via Refactor > Convert Strings to Symbols, which enables the checkbox
|
|
298
|
+
Naming: the key is camelCased and its first segment lowercased.
|
|
406
299
|
|
|
407
|
-
|
|
300
|
+
| Key | Symbol |
|
|
301
|
+
|---|---|
|
|
302
|
+
| `cart_empty` | `.cartEmpty` |
|
|
303
|
+
| `profile.privacy.header` | `.profilePrivacyHeader` |
|
|
304
|
+
| `DONE` | `.done` |
|
|
408
305
|
|
|
409
|
-
|
|
306
|
+
Keys with format specifiers turn into functions. A named positional placeholder
|
|
307
|
+
such as `%1$(nights)lld` gives a readable label; a bare `%lld` gives a generic
|
|
308
|
+
one.
|
|
410
309
|
|
|
411
|
-
|
|
|
412
|
-
|
|
413
|
-
| `
|
|
414
|
-
| `
|
|
415
|
-
| `TITLE` | `.title` |
|
|
310
|
+
| Key and value | Symbol |
|
|
311
|
+
|---|---|
|
|
312
|
+
| `stay_length` = `%1$(nights)lld nights` | `.stayLength(nights: Int)` |
|
|
313
|
+
| `welcome_user` = `Hi %@` | `.welcomeUser(_ arg1: String)` |
|
|
416
314
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
| Catalog key | Format | Generated symbol |
|
|
420
|
-
|-------------|--------|-----------------|
|
|
421
|
-
| `products_count` | `%1$(count)lld` | `.productsCount(count: Int)` |
|
|
422
|
-
| `greeting` | `%@` | `.greeting(_ param1: String)` |
|
|
423
|
-
|
|
424
|
-
You can rename parameters during refactoring for more descriptive signatures.
|
|
425
|
-
|
|
426
|
-
### Using generated symbols
|
|
315
|
+
Parameters can be renamed while refactoring.
|
|
427
316
|
|
|
428
317
|
```swift
|
|
429
|
-
|
|
430
|
-
Text(.
|
|
431
|
-
|
|
432
|
-
// Parameterized key - function
|
|
433
|
-
Text(.productsCount(count: 42))
|
|
318
|
+
Text(.cartEmpty)
|
|
319
|
+
Text(.stayLength(nights: 4))
|
|
434
320
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
// In non-SwiftUI code
|
|
439
|
-
let title = String(localized: .itemAvailable)
|
|
440
|
-
let attributed = AttributedString(localized: .greeting(userName))
|
|
321
|
+
let plain = String(localized: .cartEmpty)
|
|
322
|
+
let styled = AttributedString(localized: .welcomeUser(member.firstName))
|
|
441
323
|
```
|
|
442
324
|
|
|
443
|
-
Code completion
|
|
444
|
-
|
|
445
|
-
### Refactoring existing strings to symbols
|
|
446
|
-
|
|
447
|
-
Select one or more keys in the String Catalog editor, Control-click, and choose **Refactor > Convert Strings to Symbols**. Xcode replaces string literal usage in code with the generated symbol. This is reversible via **Convert Symbols to Strings**.
|
|
325
|
+
Code completion lists the symbols after you type `.`.
|
|
448
326
|
|
|
449
|
-
|
|
327
|
+
Converting existing literals: select the keys, Control-click, Refactor > Convert
|
|
328
|
+
Strings to Symbols. Xcode rewrites the literals at their call sites. Convert
|
|
329
|
+
Symbols to Strings goes the other way.
|
|
450
330
|
|
|
451
|
-
|
|
331
|
+
Across modules: symbols are `internal`. The usual fix is a public extension on
|
|
332
|
+
`LocalizedStringResource` that forwards to them:
|
|
452
333
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
334
|
+
```swift
|
|
335
|
+
public extension LocalizedStringResource {
|
|
336
|
+
static var sharedRetry: LocalizedStringResource { .retryButton }
|
|
337
|
+
}
|
|
338
|
+
```
|
|
457
339
|
|
|
458
|
-
|
|
340
|
+
When those wrappers become tedious, `xcstrings-tool`, an open-source Swift
|
|
341
|
+
package plugin, generates public constants for you.
|
|
459
342
|
|
|
460
|
-
|
|
343
|
+
Inside a Swift package, generated symbols read from the `.module` bundle
|
|
344
|
+
automatically, and they are only visible within the same package target.
|
|
461
345
|
|
|
462
|
-
|
|
346
|
+
## Testing
|
|
463
347
|
|
|
464
|
-
|
|
348
|
+
Scheme override: Edit Scheme > Run > Options > App Language starts the app in
|
|
349
|
+
another language without touching the system setting.
|
|
465
350
|
|
|
466
|
-
|
|
351
|
+
Pseudolanguages from the same menu:
|
|
467
352
|
|
|
468
|
-
|
|
|
469
|
-
|
|
470
|
-
| Accented
|
|
471
|
-
| Right-to-Left
|
|
472
|
-
| Double-Length
|
|
473
|
-
| Bounded String
|
|
353
|
+
| Pseudolanguage | Finds |
|
|
354
|
+
|---|---|
|
|
355
|
+
| Accented | Hard-coded strings that bypass lookup |
|
|
356
|
+
| Right-to-Left | Mirroring bugs |
|
|
357
|
+
| Double-Length | Truncation and overflow |
|
|
358
|
+
| Bounded String | Missing localizations (strings are wrapped in brackets) |
|
|
474
359
|
|
|
475
|
-
|
|
360
|
+
UI test in a specific locale:
|
|
476
361
|
|
|
477
362
|
```swift
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
### Snapshot testing per locale
|
|
492
|
-
|
|
493
|
-
Use a snapshot testing library to capture screenshots in multiple locales and compare them for layout regressions:
|
|
494
|
-
|
|
495
|
-
```swift
|
|
496
|
-
let locales = ["en_US", "de_DE", "ar_SA", "ja_JP"]
|
|
497
|
-
for locale in locales {
|
|
498
|
-
app.launchArguments = ["-AppleLanguages", "(\(locale.prefix(2)))"]
|
|
499
|
-
app.launch()
|
|
500
|
-
// Capture and compare snapshot
|
|
363
|
+
import XCTest
|
|
364
|
+
|
|
365
|
+
final class GermanCheckoutUITests: XCTestCase {
|
|
366
|
+
@MainActor
|
|
367
|
+
func testPayButtonIsLocalized() {
|
|
368
|
+
let app = XCUIApplication()
|
|
369
|
+
app.launchArguments += ["-AppleLanguages", "(de)", "-AppleLocale", "de_DE"]
|
|
370
|
+
app.launch()
|
|
371
|
+
|
|
372
|
+
let pay = app.buttons["Jetzt bezahlen"]
|
|
373
|
+
XCTAssertTrue(pay.exists)
|
|
374
|
+
XCTAssertTrue(pay.isHittable)
|
|
375
|
+
}
|
|
501
376
|
}
|
|
502
377
|
```
|
|
503
378
|
|
|
504
|
-
|
|
379
|
+
Screenshot comparison per locale: loop over `en_US`, `de_DE`, `ar_SA` and
|
|
380
|
+
`ja_JP`, set `-AppleLanguages` for each, launch, capture, compare against the
|
|
381
|
+
stored image.
|
|
505
382
|
|
|
506
|
-
|
|
383
|
+
Translation coverage in CI: parse the catalog and fail when any language entry
|
|
384
|
+
is untranslated or empty.
|
|
507
385
|
|
|
508
|
-
```
|
|
509
|
-
|
|
510
|
-
python3 -c "
|
|
386
|
+
```python
|
|
387
|
+
#!/usr/bin/env python3
|
|
511
388
|
import json, sys
|
|
512
|
-
with open('Localizable.xcstrings') as f:
|
|
513
|
-
data = json.load(f)
|
|
514
|
-
missing = []
|
|
515
|
-
for key, info in data['strings'].items():
|
|
516
|
-
for lang, loc in info.get('localizations', {}).items():
|
|
517
|
-
unit = loc.get('stringUnit', {})
|
|
518
|
-
if unit.get('state') in ('new', None) or not unit.get('value'):
|
|
519
|
-
missing.append(f'{lang}: {key}')
|
|
520
|
-
if missing:
|
|
521
|
-
print('Missing translations:')
|
|
522
|
-
for m in missing: print(f' {m}')
|
|
523
|
-
sys.exit(1)
|
|
524
|
-
print('All translations complete.')
|
|
525
|
-
"
|
|
526
|
-
```
|
|
527
|
-
|
|
528
|
-
## Migration from .strings / .stringsdict
|
|
529
389
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
390
|
+
catalog = json.load(open(sys.argv[1], encoding="utf-8"))
|
|
391
|
+
gaps = []
|
|
392
|
+
for key, entry in catalog.get("strings", {}).items():
|
|
393
|
+
for lang, loc in entry.get("localizations", {}).items():
|
|
394
|
+
units = [loc["stringUnit"]] if "stringUnit" in loc else [
|
|
395
|
+
v["stringUnit"]
|
|
396
|
+
for group in loc.get("variations", {}).values()
|
|
397
|
+
for v in group.values() if "stringUnit" in v
|
|
398
|
+
]
|
|
399
|
+
for unit in units:
|
|
400
|
+
if unit.get("state", "new") == "new" or not unit.get("value"):
|
|
401
|
+
gaps.append(f"{lang}: {key}")
|
|
402
|
+
|
|
403
|
+
print("\n".join(gaps) or "all strings translated")
|
|
404
|
+
sys.exit(1 if gaps else 0)
|
|
405
|
+
```
|
|
537
406
|
|
|
538
|
-
|
|
407
|
+
## Migrating from .strings and .stringsdict
|
|
539
408
|
|
|
540
|
-
|
|
409
|
+
Automatic: right-click the `.strings` file, choose Migrate to String Catalog...,
|
|
410
|
+
check the result, then remove the old files from the target.
|
|
541
411
|
|
|
542
|
-
|
|
543
|
-
2. Build to extract keys from code
|
|
544
|
-
3. Copy translations from old `.strings` files into the String Catalog editor
|
|
545
|
-
4. Copy plural rules from `.stringsdict` into plural variants
|
|
546
|
-
5. Remove old files
|
|
412
|
+
By hand, when a bundle setup is unusual or files come from CocoaPods:
|
|
547
413
|
|
|
548
|
-
|
|
414
|
+
1. Create a new catalog.
|
|
415
|
+
2. Build so extraction fills it.
|
|
416
|
+
3. Copy translations across from the old files.
|
|
417
|
+
4. Rebuild `.stringsdict` rules as plural variants.
|
|
418
|
+
5. Remove the legacy files.
|
|
549
419
|
|
|
550
|
-
|
|
551
|
-
- [ ] All `.stringsdict` plural rules converted to String Catalog plural variants
|
|
552
|
-
- [ ] Bundle references updated (if custom bundle was used)
|
|
553
|
-
- [ ] Build succeeds with no missing-localization warnings
|
|
554
|
-
- [ ] Test every language the app supports
|
|
555
|
-
- [ ] Remove old `.strings` and `.stringsdict` files from the target
|
|
556
|
-
- [ ] Commit the `.xcstrings` file (it is JSON, diffs well in version control)
|
|
420
|
+
Before calling it done:
|
|
557
421
|
|
|
558
|
-
|
|
422
|
+
- [ ] Every key made it across
|
|
423
|
+
- [ ] Plurals are variants now
|
|
424
|
+
- [ ] Custom bundle references point to the right place
|
|
425
|
+
- [ ] The build shows no missing-localization warnings
|
|
426
|
+
- [ ] Every language was run
|
|
427
|
+
- [ ] Legacy files are gone
|
|
428
|
+
- [ ] `.xcstrings` is committed (it diffs cleanly as JSON)
|
|
559
429
|
|
|
560
|
-
|
|
430
|
+
During the transition both formats work: lookup tries the catalog first, then
|
|
431
|
+
the `.strings` file. Delete the legacy file once you have verified the catalog.
|
|
561
432
|
|
|
562
|
-
##
|
|
433
|
+
## Practices that scale
|
|
563
434
|
|
|
564
|
-
1.
|
|
565
|
-
2.
|
|
566
|
-
3.
|
|
567
|
-
4.
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
435
|
+
1. One catalog per target as the source of truth.
|
|
436
|
+
2. A comment on every ambiguous key; translators never see the screen.
|
|
437
|
+
3. After each build, look for keys in state `new` and get them translated.
|
|
438
|
+
4. Keep `.xcstrings` in version control and review translation diffs in pull
|
|
439
|
+
requests.
|
|
440
|
+
5. Run a translation coverage check in CI.
|
|
441
|
+
6. Send `.xcloc` exports to translators at every sprint or feature merge.
|
|
442
|
+
7. Run UI tests under the double-length and RTL pseudolanguages in CI.
|
|
443
|
+
8. Prefer stable keys with generated symbols for compile-time checking and
|
|
444
|
+
autocompletion.
|